Spaces:
Running
Running
import streamlit as st | |
import pandas as pd | |
import numpy as np | |
import folium | |
from streamlit_folium import folium_static | |
import ee | |
import os | |
import json | |
import time | |
from datetime import datetime, timedelta | |
import plotly.express as px | |
import plotly.graph_objects as go | |
from PIL import Image | |
import base64 | |
from io import BytesIO | |
import matplotlib.pyplot as plt | |
import seaborn as sns | |
import altair as alt | |
from streamlit_option_menu import option_menu | |
from streamlit_lottie import st_lottie | |
import requests | |
import hydralit_components as hc | |
from streamlit_extras.colored_header import colored_header | |
from streamlit_extras.metric_cards import style_metric_cards | |
from streamlit_extras.chart_container import chart_container | |
from streamlit_extras.add_vertical_space import add_vertical_space | |
from streamlit_card import card | |
import pydeck as pdk | |
import math | |
# Page configuration with custom theme | |
st.set_page_config( | |
page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا", | |
page_icon="🌿", | |
layout="wide", | |
initial_sidebar_state="expanded" | |
) | |
# Enhanced Custom CSS with modern design and animations | |
st.markdown(""" | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100;200;300;400;500;600;700;800;900&display=swap'); | |
* { | |
font-family: 'Vazirmatn', sans-serif !important; | |
transition: all 0.3s ease-in-out; | |
} | |
.main { | |
background: linear-gradient(135deg, #f5f7fa 0%, #e4e9f2 100%); | |
background-size: 400% 400%; | |
animation: gradient 15s ease infinite; | |
} | |
@keyframes gradient { | |
0% { background-position: 0% 50%; } | |
50% { background-position: 100% 50%; } | |
100% { background-position: 0% 50%; } | |
} | |
.main-header { | |
background: linear-gradient(90deg, #1a8754 0%, #115740 100%); | |
padding: 2rem; | |
border-radius: 20px; | |
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15); | |
margin-bottom: 2.5rem; | |
position: relative; | |
overflow: hidden; | |
animation: header-glow 5s infinite alternate; | |
transform: translateY(0); | |
transition: transform 0.5s ease, box-shadow 0.5s ease; | |
} | |
.main-header:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 15px 50px rgba(26, 135, 84, 0.3); | |
} | |
@keyframes header-glow { | |
0% { box-shadow: 0 10px 40px rgba(26, 135, 84, 0.1); } | |
100% { box-shadow: 0 15px 50px rgba(26, 135, 84, 0.4); } | |
} | |
.main-header::before { | |
content: ''; | |
position: absolute; | |
top: -50%; | |
left: -50%; | |
width: 200%; | |
height: 200%; | |
background: radial-gradient(circle, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0) 70%); | |
transform: rotate(30deg); | |
z-index: 0; | |
animation: shine 8s infinite linear; | |
} | |
@keyframes shine { | |
0% { transform: rotate(30deg) translateX(-30%); } | |
100% { transform: rotate(30deg) translateX(30%); } | |
} | |
.main-header h1 { | |
color: white; | |
font-weight: 800; | |
margin: 0; | |
position: relative; | |
z-index: 1; | |
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); | |
animation: text-focus-in 1s cubic-bezier(0.550, 0.085, 0.680, 0.530) both; | |
} | |
@keyframes text-focus-in { | |
0% { filter: blur(12px); opacity: 0; } | |
100% { filter: blur(0px); opacity: 1; } | |
} | |
.main-header p { | |
color: rgba(255, 255, 255, 0.9); | |
margin: 0.5rem 0 0 0; | |
position: relative; | |
z-index: 1; | |
font-weight: 300; | |
font-size: 1.1rem; | |
animation: fade-in 1.2s cubic-bezier(0.390, 0.575, 0.565, 1.000) both; | |
animation-delay: 0.5s; | |
} | |
@keyframes fade-in { | |
0% { opacity: 0; transform: translateY(10px); } | |
100% { opacity: 1; transform: translateY(0); } | |
} | |
.stcard { | |
border-radius: 20px; | |
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08); | |
transition: all 0.4s ease; | |
overflow: hidden; | |
background: rgba(255, 255, 255, 0.9); | |
backdrop-filter: blur(10px); | |
border: 1px solid rgba(255, 255, 255, 0.5); | |
} | |
.stcard:hover { | |
transform: translateY(-10px) scale(1.02); | |
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15); | |
} | |
.stButton>button { | |
border-radius: 50px; | |
padding: 0.6rem 2rem; | |
font-weight: 600; | |
transition: all 0.4s ease; | |
border: none; | |
position: relative; | |
overflow: hidden; | |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); | |
letter-spacing: 0.5px; | |
} | |
.stButton>button::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: -100%; | |
width: 100%; | |
height: 100%; | |
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); | |
transition: all 0.6s ease; | |
} | |
.stButton>button:hover { | |
transform: translateY(-3px) scale(1.05); | |
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15); | |
} | |
.stButton>button:hover::before { | |
left: 100%; | |
} | |
.stButton>button:active { | |
transform: translateY(1px) scale(0.98); | |
} | |
.primary-btn { | |
background: linear-gradient(45deg, #1a8754 0%, #27ae60 100%); | |
color: white; | |
} | |
.secondary-btn { | |
background: white; | |
color: #1a8754; | |
border: 2px solid #1a8754 !important; | |
} | |
.metric-card { | |
background: rgba(255, 255, 255, 0.9); | |
border-radius: 20px; | |
padding: 1.8rem; | |
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08); | |
transition: all 0.4s ease; | |
text-align: center; | |
position: relative; | |
overflow: hidden; | |
backdrop-filter: blur(10px); | |
border: 1px solid rgba(255, 255, 255, 0.5); | |
z-index: 1; | |
} | |
.metric-card::before { | |
content: ''; | |
position: absolute; | |
top: -50%; | |
left: -50%; | |
width: 200%; | |
height: 200%; | |
background: radial-gradient(circle, rgba(26, 135, 84, 0.05) 0%, rgba(26, 135, 84, 0) 70%); | |
transform: rotate(30deg); | |
z-index: -1; | |
} | |
.metric-card:hover { | |
transform: translateY(-10px) scale(1.03); | |
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.12); | |
} | |
.metric-card .metric-value { | |
font-size: 3rem; | |
font-weight: 800; | |
background: linear-gradient(45deg, #1a8754, #27ae60); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
margin-bottom: 0.8rem; | |
line-height: 1; | |
animation: pulse 2s infinite; | |
} | |
@keyframes pulse { | |
0% { transform: scale(1); } | |
50% { transform: scale(1.05); } | |
100% { transform: scale(1); } | |
} | |
.metric-card .metric-label { | |
font-size: 1.1rem; | |
color: #4a5568; | |
font-weight: 500; | |
} | |
.map-container { | |
border-radius: 20px; | |
overflow: hidden; | |
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08); | |
border: 1px solid rgba(255, 255, 255, 0.5); | |
transition: all 0.4s ease; | |
} | |
.map-container:hover { | |
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15); | |
transform: translateY(-5px); | |
} | |
.stTabs [data-baseweb="tab-list"] { | |
gap: 10px; | |
background-color: rgba(255, 255, 255, 0.7); | |
padding: 10px; | |
border-radius: 50px; | |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05); | |
} | |
.stTabs [data-baseweb="tab"] { | |
border-radius: 50px; | |
padding: 10px 20px; | |
background-color: transparent; | |
transition: all 0.3s ease; | |
font-weight: 500; | |
} | |
.stTabs [data-baseweb="tab"]:hover { | |
background-color: rgba(26, 135, 84, 0.1); | |
} | |
.stTabs [aria-selected="true"] { | |
background: linear-gradient(45deg, #1a8754, #27ae60) !important; | |
color: white !important; | |
box-shadow: 0 4px 15px rgba(26, 135, 84, 0.3); | |
} | |
[data-testid="stSidebar"] { | |
background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%); | |
border-right: 1px solid rgba(0, 0, 0, 0.05); | |
box-shadow: 5px 0 15px rgba(0, 0, 0, 0.03); | |
} | |
@keyframes fadeIn { | |
0% { opacity: 0; transform: translateY(30px); } | |
100% { opacity: 1; transform: translateY(0); } | |
} | |
.animate-fadeIn { | |
animation: fadeIn 0.7s ease forwards; | |
} | |
.loading-spinner { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
height: 120px; | |
} | |
.loading-spinner::after { | |
content: ""; | |
width: 50px; | |
height: 50px; | |
border: 4px solid rgba(26, 135, 84, 0.1); | |
border-top: 4px solid #1a8754; | |
border-radius: 50%; | |
animation: spin 1s linear infinite; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.rtl { | |
direction: rtl; | |
text-align: right; | |
} | |
::-webkit-scrollbar { | |
width: 10px; | |
height: 10px; | |
} | |
::-webkit-scrollbar-track { | |
background: #f1f1f1; | |
border-radius: 10px; | |
} | |
::-webkit-scrollbar-thumb { | |
background: linear-gradient(45deg, #1a8754, #27ae60); | |
border-radius: 10px; | |
border: 2px solid #f1f1f1; | |
} | |
::-webkit-scrollbar-thumb:hover { | |
background: linear-gradient(45deg, #115740, #1a8754); | |
} | |
.tooltip { | |
position: relative; | |
display: inline-block; | |
} | |
.tooltip .tooltiptext { | |
visibility: hidden; | |
width: 150px; | |
background: rgba(0, 0, 0, 0.8); | |
color: #fff; | |
text-align: center; | |
border-radius: 10px; | |
padding: 10px; | |
position: absolute; | |
z-index: 1000; | |
bottom: 125%; | |
left: 50%; | |
margin-left: -75px; | |
opacity: 0; | |
transition: opacity 0.3s, transform 0.3s; | |
transform: translateY(10px); | |
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); | |
font-size: 0.9rem; | |
} | |
.tooltip:hover .tooltiptext { | |
visibility: visible; | |
opacity: 1; | |
transform: translateY(0); | |
} | |
.dataframe { | |
border-collapse: collapse; | |
width: 100%; | |
border-radius: 15px; | |
overflow: hidden; | |
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08); | |
animation: table-appear 0.8s ease-out; | |
} | |
@keyframes table-appear { | |
0% { opacity: 0; transform: translateY(20px); } | |
100% { opacity: 1; transform: translateY(0); } | |
} | |
.dataframe th { | |
background: linear-gradient(45deg, #1a8754, #27ae60); | |
color: white; | |
padding: 15px; | |
text-align: right; | |
font-weight: 600; | |
position: relative; | |
overflow: hidden; | |
} | |
.dataframe th::after { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: -100%; | |
width: 100%; | |
height: 100%; | |
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); | |
transition: all 0.6s ease; | |
} | |
.dataframe th:hover::after { | |
left: 100%; | |
} | |
.dataframe td { | |
padding: 12px 15px; | |
border-bottom: 1px solid rgba(0, 0, 0, 0.05); | |
transition: all 0.3s ease; | |
} | |
.dataframe tr:nth-child(even) { | |
background-color: rgba(0, 0, 0, 0.02); | |
} | |
.dataframe tr:hover { | |
background-color: rgba(26, 135, 84, 0.05); | |
transform: scale(1.01); | |
} | |
.stProgress > div > div > div > div { | |
background: linear-gradient(45deg, #1a8754, #27ae60); | |
border-radius: 10px; | |
} | |
.notification { | |
background: linear-gradient(45deg, #d1e7dd, #e8f5e9); | |
color: #0f5132; | |
padding: 1.2rem; | |
border-radius: 15px; | |
margin-bottom: 1.5rem; | |
display: flex; | |
align-items: center; | |
animation: slideIn 0.7s ease, pulse-light 2s infinite alternate; | |
box-shadow: 0 8px 20px rgba(15, 81, 50, 0.1); | |
border-left: 5px solid #1a8754; | |
} | |
@keyframes pulse-light { | |
0% { box-shadow: 0 8px 20px rgba(15, 81, 50, 0.1); } | |
100% { box-shadow: 0 8px 30px rgba(15, 81, 50, 0.2); } | |
} | |
@keyframes slideIn { | |
0% { transform: translateX(100%); opacity: 0; } | |
100% { transform: translateX(0); opacity: 1; } | |
} | |
.notification-icon { | |
margin-right: 1rem; | |
font-size: 1.5rem; | |
color: #1a8754; | |
} | |
.custom-select { | |
background-color: white; | |
border-radius: 15px; | |
padding: 0.8rem; | |
border: 1px solid rgba(0, 0, 0, 0.1); | |
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); | |
transition: all 0.3s ease; | |
} | |
.custom-select:focus { | |
border-color: #1a8754; | |
box-shadow: 0 5px 20px rgba(26, 135, 84, 0.15); | |
transform: translateY(-2px); | |
} | |
.glass-card { | |
background: rgba(255, 255, 255, 0.7); | |
backdrop-filter: blur(15px); | |
-webkit-backdrop-filter: blur(15px); | |
border-radius: 20px; | |
border: 1px solid rgba(255, 255, 255, 0.5); | |
padding: 2rem; | |
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); | |
transition: all 0.4s ease; | |
animation: card-appear 0.8s ease-out; | |
} | |
@keyframes card-appear { | |
0% { opacity: 0; transform: translateY(30px) scale(0.95); } | |
100% { opacity: 1; transform: translateY(0) scale(1); } | |
} | |
.glass-card:hover { | |
transform: translateY(-10px); | |
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15); | |
border-color: rgba(26, 135, 84, 0.3); | |
} | |
.neumorphic-card { | |
background: #f0f0f3; | |
border-radius: 20px; | |
box-shadow: 15px 15px 30px #d1d1d4, -15px -15px 30px #ffffff; | |
padding: 2rem; | |
transition: all 0.4s ease; | |
} | |
.neumorphic-card:hover { | |
box-shadow: 20px 20px 40px #d1d1d4, -20px -20px 40px #ffffff; | |
} | |
.gradient-text { | |
background: linear-gradient(45deg, #1a8754, #27ae60); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
font-weight: 800; | |
font-size: 2rem; | |
letter-spacing: 0.5px; | |
animation: text-shimmer 2s infinite; | |
} | |
@keyframes text-shimmer { | |
0% { background-position: 0% 50%; } | |
100% { background-position: 100% 50%; } | |
} | |
.pulse-animation { | |
animation: pulse 2s infinite; | |
} | |
.stRadio > div { | |
display: flex; | |
gap: 15px; | |
background: rgba(255, 255, 255, 0.7); | |
padding: 10px; | |
border-radius: 50px; | |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05); | |
} | |
.stRadio label { | |
cursor: pointer; | |
background-color: transparent; | |
padding: 0.7rem 1.5rem; | |
border-radius: 50px; | |
transition: all 0.3s ease; | |
font-weight: 500; | |
} | |
.stRadio label:hover { | |
background-color: rgba(26, 135, 84, 0.1); | |
} | |
.stRadio input { | |
display: none; | |
} | |
.stRadio input:checked + label { | |
background: linear-gradient(45deg, #1a8754, #27ae60); | |
color: white; | |
box-shadow: 0 4px 15px rgba(26, 135, 84, 0.3); | |
} | |
.stSelectbox, .stNumberInput { | |
background-color: rgba(255, 255, 255, 0.7); | |
border-radius: 15px; | |
padding: 15px; | |
margin: 15px 0; | |
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); | |
transition: all 0.3s ease; | |
border: 1px solid rgba(255, 255, 255, 0.5); | |
} | |
.stSelectbox:hover, .stNumberInput:hover { | |
transform: translateY(-3px); | |
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08); | |
} | |
.custom-card { | |
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); | |
padding: 25px; | |
border-radius: 20px; | |
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); | |
margin: 15px 0; | |
transition: all 0.4s ease; | |
border: 1px solid rgba(255, 255, 255, 0.5); | |
position: relative; | |
overflow: hidden; | |
} | |
.custom-card::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 5px; | |
background: linear-gradient(90deg, #1a8754, #27ae60); | |
} | |
.custom-card:hover { | |
transform: translateY(-10px) scale(1.02); | |
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.12); | |
} | |
.metric-container { | |
display: flex; | |
justify-content: space-between; | |
flex-wrap: wrap; | |
gap: 20px; | |
} | |
.metric-card { | |
background: linear-gradient(135deg, #1a8754 0%, #27ae60 100%); | |
color: white; | |
padding: 25px; | |
border-radius: 20px; | |
margin: 5px; | |
flex: 1; | |
min-width: 200px; | |
text-align: center; | |
box-shadow: 0 10px 30px rgba(26, 135, 84, 0.2); | |
transition: all 0.4s ease; | |
position: relative; | |
overflow: hidden; | |
z-index: 1; | |
} | |
.metric-card::before { | |
content: ''; | |
position: absolute; | |
top: -50%; | |
left: -50%; | |
width: 200%; | |
height: 200%; | |
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%); | |
transform: rotate(30deg); | |
z-index: -1; | |
} | |
.metric-card:hover { | |
transform: translateY(-10px) scale(1.05); | |
box-shadow: 0 15px 40px rgba(26, 135, 84, 0.3); | |
} | |
.metric-card h3 { | |
font-size: 2.5rem; | |
font-weight: 800; | |
margin-bottom: 10px; | |
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); | |
} | |
.metric-card p { | |
font-size: 1.1rem; | |
opacity: 0.9; | |
} | |
/* Animated buttons */ | |
.animated-btn { | |
background: linear-gradient(45deg, #1a8754, #27ae60); | |
color: white; | |
border: none; | |
padding: 12px 30px; | |
border-radius: 50px; | |
font-weight: 600; | |
font-size: 1rem; | |
cursor: pointer; | |
box-shadow: 0 8px 20px rgba(26, 135, 84, 0.3); | |
transition: all 0.4s ease; | |
position: relative; | |
overflow: hidden; | |
display: inline-block; | |
text-align: center; | |
letter-spacing: 0.5px; | |
z-index: 1; | |
} | |
.animated-btn::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: -100%; | |
width: 100%; | |
height: 100%; | |
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); | |
transition: all 0.6s ease; | |
z-index: -1; | |
} | |
.animated-btn:hover { | |
transform: translateY(-5px) scale(1.05); | |
box-shadow: 0 12px 30px rgba(26, 135, 84, 0.4); | |
} | |
.animated-btn:hover::before { | |
left: 100%; | |
} | |
.animated-btn:active { | |
transform: translateY(2px) scale(0.98); | |
} | |
/* Floating elements */ | |
.floating { | |
animation: floating 3s ease-in-out infinite; | |
} | |
@keyframes floating { | |
0% { transform: translateY(0px); } | |
50% { transform: translateY(-15px); } | |
100% { transform: translateY(0px); } | |
} | |
/* 3D Card effect */ | |
.card-3d { | |
transition: all 0.5s ease; | |
transform-style: preserve-3d; | |
perspective: 1000px; | |
} | |
.card-3d:hover { | |
transform: rotateY(10deg) rotateX(10deg); | |
} | |
/* Glowing effect */ | |
.glow { | |
position: relative; | |
} | |
.glow::after { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
border-radius: inherit; | |
box-shadow: 0 0 20px 5px rgba(26, 135, 84, 0.5); | |
opacity: 0; | |
transition: opacity 0.3s ease; | |
} | |
.glow:hover::after { | |
opacity: 1; | |
} | |
/* Ripple effect */ | |
.ripple { | |
position: relative; | |
overflow: hidden; | |
} | |
.ripple::after { | |
content: ''; | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
width: 0; | |
height: 0; | |
background: rgba(255, 255, 255, 0.4); | |
border-radius: 50%; | |
transform: translate(-50%, -50%); | |
opacity: 0; | |
} | |
.ripple:active::after { | |
width: 300%; | |
height: 300%; | |
opacity: 1; | |
transition: all 0.6s ease; | |
} | |
/* Neon glow */ | |
.neon-glow { | |
box-shadow: 0 0 10px #1a8754, 0 0 20px #1a8754, 0 0 30px #1a8754; | |
animation: neon-pulse 2s infinite alternate; | |
} | |
@keyframes neon-pulse { | |
0% { box-shadow: 0 0 10px #1a8754, 0 0 20px #1a8754, 0 0 30px #1a8754; } | |
100% { box-shadow: 0 0 15px #1a8754, 0 0 30px #1a8754, 0 0 45px #1a8754; } | |
} | |
/* Particle background */ | |
.particle-bg { | |
position: relative; | |
} | |
.particle-bg::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background-image: | |
radial-gradient(circle, #1a8754 1px, transparent 1px), | |
radial-gradient(circle, #1a8754 1px, transparent 1px); | |
background-size: 30px 30px; | |
background-position: 0 0, 15px 15px; | |
opacity: 0.1; | |
z-index: -1; | |
} | |
/* Animated icons */ | |
.animated-icon { | |
display: inline-block; | |
animation: icon-bounce 2s infinite; | |
} | |
@keyframes icon-bounce { | |
0%, 100% { transform: translateY(0); } | |
50% { transform: translateY(-10px); } | |
} | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# Initialize Earth Engine | |
def initialize_earth_engine(): | |
try: | |
service_account = 'dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com' | |
credentials_dict = { | |
"type": "service_account", | |
"project_id": "ee-esmaeilkiani13877", | |
"private_key_id": "cfdea6eaf4115cb6462626743e4b15df85fd0c7f", | |
"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", | |
"client_email": "dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com", | |
"client_id": "113062529451626176784", | |
"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", | |
"universe_domain": "googleapis.com" | |
} | |
credentials_file = 'ee-esmaeilkiani13877-cfdea6eaf411.json' | |
with open(credentials_file, 'w') as f: | |
json.dump(credentials_dict, f) | |
credentials = ee.ServiceAccountCredentials(service_account, credentials_file) | |
ee.Initialize(credentials) | |
os.remove(credentials_file) | |
return True | |
except Exception as e: | |
st.error(f"خطا در اتصال به Earth Engine: {e}") | |
return False | |
# Load data | |
def load_farm_data(): | |
try: | |
df = pd.read_csv("پایگاه داده (1).csv") | |
return df | |
except Exception as e: | |
st.error(f"خطا در بارگذاری دادههای مزارع: {e}") | |
return pd.DataFrame() | |
def load_coordinates_data(): | |
try: | |
df = pd.read_csv("farm_coordinates.csv") | |
return df | |
except Exception as e: | |
st.error(f"خطا در بارگذاری دادههای مختصات: {e}") | |
return pd.DataFrame() | |
# Load animation JSON | |
def load_lottie_url(url: str): | |
r = requests.get(url) | |
if r.status_code != 200: | |
return None | |
return r.json() | |
# Function to get weather data | |
def get_weather_data(lat, lon, api_key): | |
url = f"http://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={api_key}&units=metric" | |
response = requests.get(url) | |
if response.status_code == 200: | |
return response.json() | |
else: | |
return None | |
# Function to estimate water requirement | |
def estimate_water_requirement(farm_id, date_str): | |
api_key = "ed47316a45379e2221a75f813229fb46" | |
farm_row = coordinates_df[coordinates_df['مزرعه'] == farm_id].iloc[0] | |
lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی'] | |
weather_data = get_weather_data(lat, lon, api_key) | |
if weather_data: | |
temperature = weather_data['main']['temp'] | |
humidity = weather_data['main']['humidity'] | |
water_requirement = (temperature - 20) * 0.5 + (100 - humidity) * 0.1 | |
return max(0, water_requirement) | |
else: | |
return None | |
# Function to calculate indices from GEE | |
def calculate_indices(farm_id, date_str): | |
try: | |
farm_row = coordinates_df[coordinates_df['مزرعه'] == farm_id].iloc[0] | |
lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی'] | |
date_obj = datetime.strptime(date_str, '%Y-%m-%d') | |
start_date = (date_obj - timedelta(days=7)).strftime('%Y-%m-%d') | |
end_date = date_obj.strftime('%Y-%m-%d') | |
region = ee.Geometry.Point([lon, lat]).buffer(500) | |
s2 = ee.ImageCollection('COPERNICUS/S2_SR') \ | |
.filterDate(start_date, end_date) \ | |
.filterBounds(region) \ | |
.sort('CLOUDY_PIXEL_PERCENTAGE') \ | |
.first() | |
ndvi = s2.normalizedDifference(['B8', 'B4']).rename('NDVI') | |
ndwi = s2.normalizedDifference(['B3', 'B8']).rename('NDWI') | |
ndvi_value = ndvi.reduceRegion( | |
reducer=ee.Reducer.mean(), | |
geometry=region, | |
scale=10 | |
).get('NDVI').getInfo() | |
ndwi_value = ndwi.reduceRegion( | |
reducer=ee.Reducer.mean(), | |
geometry=region, | |
scale=10 | |
).get('NDWI').getInfo() | |
lai_value = 6 * ndvi_value if ndvi_value else None | |
chl_value = 50 * ndvi_value if ndvi_value else None | |
return { | |
'NDVI': ndvi_value if ndvi_value else 0, | |
'NDWI': ndwi_value if ndwi_value else 0, | |
'LAI': min(max(lai_value, 0), 6) if lai_value else 0, | |
'CHL': chl_value if chl_value else 0 | |
} | |
except Exception as e: | |
st.error(f"خطا در محاسبه شاخصها برای مزرعه {farm_id}: {e}") | |
return {'NDVI': 0, 'NDWI': 0, 'LAI': 0, 'CHL': 0} | |
# Create Earth Engine map with NDVI | |
def create_ee_map(farm_id, date_str, layer_type="NDVI"): | |
try: | |
farm_row = coordinates_df[coordinates_df['مزرعه'] == farm_id].iloc[0] | |
lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی'] | |
m = folium.Map(location=[lat, lon], zoom_start=14, tiles='CartoDB positron') | |
date_obj = datetime.strptime(date_str, '%Y-%m-%d') | |
start_date = (date_obj - timedelta(days=7)).strftime('%Y-%m-%d') | |
end_date = date_obj.strftime('%Y-%m-%d') | |
region = ee.Geometry.Point([lon, lat]).buffer(1500) | |
s2 = ee.ImageCollection('COPERNICUS/S2_SR') \ | |
.filterDate(start_date, end_date) \ | |
.filterBounds(region) \ | |
.sort('CLOUDY_PIXEL_PERCENTAGE') \ | |
.first() | |
if layer_type == "NDVI": | |
index = s2.normalizedDifference(['B8', 'B4']).rename('NDVI') | |
viz_params = { | |
'min': -0.2, | |
'max': 0.8, | |
'palette': ['#ff0000', '#ffd700', '#32cd32'] # قرمز، زرد، سبز | |
} | |
legend_title = 'شاخص پوشش گیاهی (NDVI)' | |
map_id_dict = ee.Image(index).getMapId(viz_params) | |
folium.TileLayer( | |
tiles=map_id_dict['tile_fetcher'].url_format, | |
attr='Google Earth Engine', | |
name=layer_type, | |
overlay=True, | |
control=True | |
).add_to(m) | |
folium.Marker( | |
[lat, lon], | |
popup=f'مزرعه {farm_id}', | |
tooltip=f'مزرعه {farm_id}', | |
icon=folium.Icon(color='green', icon='leaf') | |
).add_to(m) | |
folium.Circle( | |
[lat, lon], | |
radius=1500, | |
color='green', | |
fill=True, | |
fill_color='green', | |
fill_opacity=0.1 | |
).add_to(m) | |
folium.LayerControl().add_to(m) | |
legend_html = ''' | |
<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; | |
font-family: 'Vazirmatn', sans-serif;"> | |
<div style="font-size: 14px; font-weight: bold; margin-bottom: 5px;">راهنما: شاخص NDVI</div> | |
<div style="display: flex; align-items: center; margin-bottom: 5px;"> | |
<div style="background: #ff0000; width: 20px; height: 20px; margin-left: 5px;"></div> | |
<span>ضعیف (NDVI ≤ 0.3)</span> | |
</div> | |
<div style="display: flex; align-items: center; margin-bottom: 5px;"> | |
<div style="background: #ffd700; width: 20px; height: 20px; margin-left: 5px;"></div> | |
<span>متوسط (0.3 < NDVI ≤ 0.6)</span> | |
</div> | |
<div style="display: flex; align-items: center;"> | |
<div style="background: #32cd32; width: 20px; height: 20px; margin-left: 5px;"></div> | |
<span>سالم (NDVI > 0.6)</span> | |
</div> | |
</div> | |
''' | |
m.get_root().html.add_child(folium.Element(legend_html)) | |
return m | |
except Exception as e: | |
st.error(f"خطا در ایجاد نقشه: {e}") | |
return None | |
# Generate mock growth data | |
def generate_mock_growth_data(farm_data, selected_variety="all", selected_age="all"): | |
weeks = list(range(1, 23)) | |
filtered_farms = farm_data | |
if selected_variety != "all": | |
filtered_farms = filtered_farms[filtered_farms['واریته'] == selected_variety] | |
if selected_age != "all": | |
filtered_farms = filtered_farms[filtered_farms['سن'] == selected_age] | |
farm_growth_data = [] | |
for _, farm in filtered_farms.iterrows(): | |
base_height = np.random.uniform(50, 100) | |
growth_rate = np.random.uniform(5, 15) | |
growth_data = { | |
'farm_id': farm['مزرعه'], | |
'variety': farm['واریته'], | |
'age': farm['سن'], | |
'weeks': weeks, | |
'heights': [round(base_height + growth_rate * week) for week in weeks] | |
} | |
farm_growth_data.append(growth_data) | |
if farm_growth_data: | |
avg_heights = [] | |
for week in weeks: | |
week_heights = [farm['heights'][week-1] for farm in farm_growth_data] | |
avg_heights.append(round(sum(week_heights) / len(week_heights))) | |
avg_growth_data = { | |
'farm_id': 'میانگین', | |
'variety': 'همه', | |
'age': 'همه', | |
'weeks': weeks, | |
'heights': avg_heights | |
} | |
return { | |
'individual': farm_growth_data, | |
'average': avg_growth_data | |
} | |
else: | |
return { | |
'individual': [], | |
'average': { | |
'farm_id': 'میانگین', | |
'variety': 'همه', | |
'age': 'همه', | |
'weeks': weeks, | |
'heights': [0] * len(weeks) | |
} | |
} | |
# Calculate statistics for a farm | |
def calculate_farm_stats(farm_id, layer_type="NDVI"): | |
if layer_type == "NDVI": | |
mean = round(np.random.uniform(0.6, 0.8), 2) | |
min_val = round(mean - np.random.uniform(0.2, 0.3), 2) | |
max_val = round(mean + np.random.uniform(0.1, 0.2), 2) | |
std_dev = round(np.random.uniform(0.05, 0.15), 2) | |
elif layer_type == "NDMI": | |
mean = round(np.random.uniform(0.3, 0.5), 2) | |
min_val = round(mean - np.random.uniform(0.2, 0.3), 2) | |
max_val = round(mean + np.random.uniform(0.1, 0.2), 2) | |
std_dev = round(np.random.uniform(0.05, 0.15), 2) | |
elif layer_type == "EVI": | |
mean = round(np.random.uniform(0.4, 0.6), 2) | |
min_val = round(mean - np.random.uniform(0.2, 0.3), 2) | |
max_val = round(mean + np.random.uniform(0.1, 0.2), 2) | |
std_dev = round(np.random.uniform(0.05, 0.15), 2) | |
elif layer_type == "NDWI": | |
mean = round(np.random.uniform(-0.1, 0.1), 2) | |
min_val = round(mean - np.random.uniform(0.2, 0.3), 2) | |
max_val = round(mean + np.random.uniform(0.1, 0.2), 2) | |
std_dev = round(np.random.uniform(0.05, 0.15), 2) | |
elif layer_type == "SoilMoisture": | |
mean = round(np.random.uniform(-20, -10), 2) | |
min_val = round(mean - np.random.uniform(5, 10), 2) | |
max_val = round(mean + np.random.uniform(5, 10), 2) | |
std_dev = round(np.random.uniform(2, 5), 2) | |
hist_data = np.random.normal(mean, std_dev, 1000) | |
hist_data = np.clip(hist_data, min_val, max_val) | |
return { | |
'mean': mean, | |
'min': min_val, | |
'max': max_val, | |
'std_dev': std_dev, | |
'histogram_data': hist_data | |
} | |
# Initialize Earth Engine | |
ee_initialized = initialize_earth_engine() | |
# Load data | |
farm_df = load_farm_data() | |
coordinates_df = load_coordinates_data() | |
# Load animations | |
lottie_farm = load_lottie_url('https://assets5.lottiefiles.com/packages/lf20_ystsffqy.json') | |
lottie_analysis = load_lottie_url('https://assets3.lottiefiles.com/packages/lf20_qp1q7mct.json') | |
lottie_report = load_lottie_url('https://assets9.lottiefiles.com/packages/lf20_vwcugezu.json') | |
# Create session state for storing data | |
if 'heights_df' not in st.session_state: | |
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' | |
]) | |
# Main header | |
st.markdown('<div class="main-header">', unsafe_allow_html=True) | |
st.markdown('<h1>سامانه هوشمند پایش مزارع نیشکر دهخدا</h1>', unsafe_allow_html=True) | |
st.markdown('<p>پلتفرم جامع مدیریت، پایش و تحلیل دادههای مزارع نیشکر با استفاده از تصاویر ماهوارهای و هوش مصنوعی</p>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
# Create a modern navigation menu | |
selected = option_menu( | |
menu_title=None, | |
options=["داشبورد", "نقشه مزارع", "ورود اطلاعات", "تحلیل دادهها", "گزارشگیری", "تنظیمات"], | |
icons=["speedometer2", "map", "pencil-square", "graph-up", "file-earmark-text", "gear"], | |
menu_icon="cast", | |
default_index=0, | |
orientation="horizontal", | |
styles={ | |
"container": {"padding": "0!important", "background-color": "transparent", "margin-bottom": "20px"}, | |
"icon": {"color": "#1a8754", "font-size": "18px"}, | |
"nav-link": {"font-size": "16px", "text-align": "center", "margin":"0px", "--hover-color": "#e9f7ef", "border-radius": "10px"}, | |
"nav-link-selected": {"background-color": "#1a8754", "color": "white", "font-weight": "600"}, | |
} | |
) | |
# Dashboard | |
if selected == "داشبورد": | |
col1, col2, col3, col4 = st.columns(4) | |
with col1: | |
st.markdown('<div class="metric-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-value">{len(farm_df)}</div>', unsafe_allow_html=True) | |
st.markdown('<div class="metric-label">تعداد مزارع</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col2: | |
active_farms = int(len(farm_df) * 0.85) | |
st.markdown('<div class="metric-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-value">{active_farms}</div>', unsafe_allow_html=True) | |
st.markdown('<div class="metric-label">مزارع فعال</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col3: | |
avg_height = 175 | |
st.markdown('<div class="metric-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-value">{avg_height} cm</div>', unsafe_allow_html=True) | |
st.markdown('<div class="metric-label">میانگین ارتفاع</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col4: | |
avg_moisture = 68 | |
st.markdown('<div class="metric-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-value">{avg_moisture}%</div>', unsafe_allow_html=True) | |
st.markdown('<div class="metric-label">میانگین رطوبت</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
tab1, tab2, tab3, tab4 = st.tabs(["نمای کلی", "نقشه مزارع", "نمودارها", "دادهها"]) | |
with tab1: | |
st.markdown("### توزیع واریتهها و سن محصول") | |
col1, col2 = st.columns(2) | |
with col1: | |
variety_counts = farm_df['واریته'].value_counts().reset_index() | |
variety_counts.columns = ['واریته', 'تعداد'] | |
fig = px.pie( | |
variety_counts, | |
values='تعداد', | |
names='واریته', | |
title='توزیع واریتهها', | |
color_discrete_sequence=px.colors.sequential.Greens_r | |
) | |
fig.update_traces(textposition='inside', textinfo='percent+label') | |
fig.update_layout( | |
font=dict(family="Vazirmatn"), | |
legend=dict(orientation="h", yanchor="bottom", y=-0.3, xanchor="center", x=0.5) | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
with col2: | |
age_counts = farm_df['سن'].value_counts().reset_index() | |
age_counts.columns = ['سن', 'تعداد'] | |
fig = px.pie( | |
age_counts, | |
values='تعداد', | |
names='سن', | |
title='توزیع سن محصول', | |
color_discrete_sequence=px.colors.sequential.Blues_r | |
) | |
fig.update_traces(textposition='inside', textinfo='percent+label') | |
fig.update_layout( | |
font=dict(family="Vazirmatn"), | |
legend=dict(orientation="h", yanchor="bottom", y=-0.3, xanchor="center", x=0.5) | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
st.markdown("### اطلاعات کلی مزارع") | |
total_area = farm_df['مساحت'].astype(float).sum() | |
col1, col2, col3 = st.columns(3) | |
col1.metric("تعداد کل مزارع", f"{len(farm_df)}") | |
col2.metric("مساحت کل (هکتار)", f"{total_area:.2f}") | |
col3.metric("تعداد کانالها", f"{farm_df['کانال'].nunique()}") | |
st.markdown('<hr style="height:2px;border:none;color:#1a8754;background-color:#1a8754;margin:30px 0;">', unsafe_allow_html=True) | |
st_lottie(lottie_farm, height=300, key="farm_animation") | |
with tab2: | |
st.markdown("### نقشه مزارع") | |
if coordinates_df is not None and not coordinates_df.empty: | |
m = folium.Map(location=[31.45, 48.72], zoom_start=12, tiles='CartoDB positron') | |
for _, farm in coordinates_df.iterrows(): | |
lat = farm['عرض جغرافیایی'] | |
lon = farm['طول جغرافیایی'] | |
name = farm['مزرعه'] | |
farm_info = farm_df[farm_df['مزرعه'] == name] | |
if not farm_info.empty: | |
variety = farm_info['واریته'].iloc[0] | |
age = farm_info['سن'].iloc[0] | |
area = farm_info['مساحت'].iloc[0] | |
popup_text = f""" | |
<div style="direction: rtl; text-align: right; font-family: 'Vazirmatn', sans-serif;"> | |
<h4>مزرعه {name}</h4> | |
<p>واریته: {variety}</p> | |
<p>سن: {age}</p> | |
<p>مساحت: {area} هکتار</p> | |
</div> | |
""" | |
else: | |
popup_text = f"<div style='direction: rtl;'>مزرعه {name}</div>" | |
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) | |
st.markdown('<div class="map-container">', unsafe_allow_html=True) | |
folium_static(m, width=1000, height=600) | |
st.markdown('</div>', unsafe_allow_html=True) | |
else: | |
st.warning("دادههای مختصات در دسترس نیست.") | |
with tab3: | |
st.markdown("### نمودار رشد هفتگی") | |
col1, col2 = st.columns(2) | |
with col1: | |
selected_variety = st.selectbox( | |
"انتخاب واریته", | |
["all"] + list(farm_df['واریته'].unique()), | |
format_func=lambda x: "همه واریتهها" if x == "all" else x | |
) | |
with col2: | |
selected_age = st.selectbox( | |
"انتخاب سن", | |
["all"] + list(farm_df['سن'].unique()), | |
format_func=lambda x: "همه سنین" if x == "all" else x | |
) | |
growth_data = generate_mock_growth_data(farm_df, selected_variety, selected_age) | |
chart_tab1, chart_tab2 = st.tabs(["میانگین رشد", "رشد مزارع فردی"]) | |
with chart_tab1: | |
avg_data = growth_data['average'] | |
fig = go.Figure() | |
fig.add_trace(go.Scatter( | |
x=avg_data['weeks'], | |
y=avg_data['heights'], | |
mode='lines+markers', | |
name='میانگین رشد', | |
line=dict(color='#1a8754', width=3), | |
marker=dict(size=8, color='#1a8754') | |
)) | |
fig.update_layout( | |
title='میانگین رشد هفتگی', | |
xaxis_title='هفته', | |
yaxis_title='ارتفاع (سانتیمتر)', | |
font=dict(family='Vazirmatn', size=14), | |
hovermode='x unified', | |
template='plotly_white', | |
height=500 | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
with chart_tab2: | |
if growth_data['individual']: | |
fig = go.Figure() | |
colors = ['#1a8754', '#1976d2', '#e65100', '#9c27b0', '#d32f2f'] | |
for i, farm_data in enumerate(growth_data['individual'][:5]): | |
fig.add_trace(go.Scatter( | |
x=farm_data['weeks'], | |
y=farm_data['heights'], | |
mode='lines+markers', | |
name=f"مزرعه {farm_data['farm_id']}", | |
line=dict(color=colors[i % len(colors)], width=2), | |
marker=dict(size=6, color=colors[i % len(colors)]) | |
)) | |
fig.update_layout( | |
title='رشد هفتگی مزارع فردی', | |
xaxis_title='هفته', | |
yaxis_title='ارتفاع (سانتیمتر)', | |
font=dict(family='Vazirmatn', size=14), | |
hovermode='x unified', | |
template='plotly_white', | |
height=500 | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
else: | |
st.warning("دادهای برای نمایش وجود ندارد.") | |
with tab4: | |
st.markdown("### دادههای مزارع") | |
search_term = st.text_input("جستجو در دادهها", placeholder="نام مزرعه، واریته، سن و...") | |
if search_term: | |
filtered_df = farm_df[ | |
farm_df['مزرعه'].astype(str).str.contains(search_term) | | |
farm_df['واریته'].astype(str).str.contains(search_term) | | |
farm_df['سن'].astype(str).str.contains(search_term) | | |
farm_df['کانال'].astype(str).str.contains(search_term) | |
] | |
else: | |
filtered_df = farm_df | |
if not filtered_df.empty: | |
csv = filtered_df.to_csv(index=False).encode('utf-8') | |
st.download_button( | |
label="دانلود دادهها (CSV)", | |
data=csv, | |
file_name="farm_data.csv", | |
mime="text/csv", | |
) | |
st.dataframe( | |
filtered_df, | |
use_container_width=True, | |
height=400, | |
hide_index=True | |
) | |
st.info(f"نمایش {len(filtered_df)} مزرعه از {len(farm_df)} مزرعه") | |
else: | |
st.warning("هیچ دادهای یافت نشد.") | |
# Map Page | |
elif selected == "نقشه مزارع": | |
st.markdown("## نقشه مزارع با شاخصهای ماهوارهای") | |
col1, col2 = st.columns([1, 3]) | |
with col1: | |
st.markdown('<div class="glass-card">', unsafe_allow_html=True) | |
st.markdown("### تنظیمات نقشه") | |
selected_farm = st.selectbox( | |
"انتخاب مزرعه", | |
options=coordinates_df['مزرعه'].tolist(), | |
index=0, | |
format_func=lambda x: f"مزرعه {x}" | |
) | |
selected_date = st.date_input( | |
"انتخاب تاریخ", | |
value=datetime.now(), | |
format="YYYY-MM-DD" | |
) | |
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] | |
) | |
generate_map = st.button( | |
"تولید نقشه", | |
type="primary", | |
use_container_width=True | |
) | |
st.markdown('<hr style="margin: 20px 0;">', unsafe_allow_html=True) | |
st.markdown("### راهنمای شاخصها") | |
with st.expander("شاخص پوشش گیاهی (NDVI)", expanded=selected_layer == "NDVI"): | |
st.markdown(""" | |
**شاخص تفاضل نرمالشده پوشش گیاهی (NDVI)** معیاری برای سنجش سلامت و تراکم پوشش گیاهی است. | |
- **مقادیر بالا (0.6 تا 1.0)**: پوشش گیاهی متراکم و سالم | |
- **مقادیر متوسط (0.2 تا 0.6)**: پوشش گیاهی متوسط | |
- **مقادیر پایین (-1.0 تا 0.2)**: پوشش گیاهی کم یا خاک لخت | |
فرمول: NDVI = (NIR - RED) / (NIR + RED) | |
""") | |
with st.expander("شاخص رطوبت (NDMI)", expanded=selected_layer == "NDMI"): | |
st.markdown(""" | |
**شاخص تفاضل نرمالشده رطوبت (NDMI)** برای ارزیابی محتوای رطوبت گیاهان استفاده میشود. | |
- **مقادیر بالا (0.4 تا 1.0)**: محتوای رطوبت بالا | |
- **مقادیر متوسط (0.0 تا 0.4)**: محتوای رطوبت متوسط | |
- **مقادیر پایین (-1.0 تا 0.0)**: محتوای رطوبت کم | |
فرمول: NDMI = (NIR - SWIR) / (NIR + SWIR) | |
""") | |
with st.expander("شاخص پیشرفته گیاهی (EVI)", expanded=selected_layer == "EVI"): | |
st.markdown(""" | |
**شاخص پیشرفته پوشش گیاهی (EVI)** نسخه بهبودیافته NDVI است که حساسیت کمتری به اثرات خاک و اتمسفر دارد. | |
- **مقادیر بالا (0.4 تا 1.0)**: پوشش گیاهی متراکم و سالم | |
- **مقادیر متوسط (0.2 تا 0.4)**: پوشش گیاهی متوسط | |
- **مقادیر پایین (0.0 تا 0.2)**: پوشش گیاهی کم | |
فرمول: EVI = 2.5 * ((NIR - RED) / (NIR + 6*RED - 7.5*BLUE + 1)) | |
""") | |
with st.expander("شاخص آب (NDWI)", expanded=selected_layer == "NDWI"): | |
st.markdown(""" | |
**شاخص تفاضل نرمالشده آب (NDWI)** برای شناسایی پهنههای آبی و ارزیابی محتوای آب در گیاهان استفاده میشود. | |
- **مقادیر بالا (0.3 تا 1.0)**: پهنههای آبی | |
- **مقادیر متوسط (0.0 تا 0.3)**: محتوای آب متوسط | |
- **مقادیر پایین (-1.0 تا 0.0)**: محتوای آب کم یا خاک خشک | |
فرمول: NDWI = (GREEN - NIR) / (GREEN + NIR) | |
""") | |
with st.expander("رطوبت خاک (Soil Moisture)", expanded=selected_layer == "SoilMoisture"): | |
st.markdown(""" | |
**رطوبت خاک (Soil Moisture)** با استفاده از دادههای راداری Sentinel-1 سطح رطوبت خاک را به صورت داینامیک بررسی میکند. | |
- **مقادیر بالا**: رطوبت خاک بالا | |
- **مقادیر پایین**: رطوبت خاک کم | |
این شاخص به مدیریت بهتر منابع آب کمک میکند. | |
""") | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col2: | |
map_tab, stats_tab = st.tabs(["نقشه", "آمار و تحلیل"]) | |
with map_tab: | |
st.markdown('<div class="map-container">', unsafe_allow_html=True) | |
if generate_map or 'last_map' not in st.session_state: | |
with st.spinner('در حال تولید نقشه...'): | |
m = create_ee_map( | |
selected_farm, | |
selected_date.strftime('%Y-%m-%d'), | |
selected_layer | |
) | |
if m: | |
st.session_state.last_map = m | |
folium_static(m, width=800, height=600) | |
st.success(f"نقشه {selected_layer} برای مزرعه {selected_farm} با موفقیت تولید شد.") | |
else: | |
st.error("خطا در تولید نقشه. لطفاً دوباره تلاش کنید.") | |
elif 'last_map' in st.session_state: | |
folium_static(st.session_state.last_map, width=800, height=600) | |
st.markdown('</div>', unsafe_allow_html=True) | |
st.info(""" | |
**نکته:** این نقشه بر اساس تصاویر Sentinel-2 و Sentinel-1 تولید شده است. | |
برای دقت بیشتر، تاریخی با ابرناکی کم انتخاب کنید. | |
""") | |
with stats_tab: | |
if 'last_map' in st.session_state: | |
stats = calculate_farm_stats(selected_farm, selected_layer) | |
col1, col2, col3, col4 = st.columns(4) | |
with col1: | |
st.markdown('<div class="metric-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-value">{stats["mean"]}</div>', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-label">میانگین {selected_layer}</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col2: | |
st.markdown('<div class="metric-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-value">{stats["max"]}</div>', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-label">حداکثر {selected_layer}</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col3: | |
st.markdown('<div class="metric-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-value">{stats["min"]}</div>', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-label">حداقل {selected_layer}</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col4: | |
st.markdown('<div class="metric-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-value">{stats["std_dev"]}</div>', unsafe_allow_html=True) | |
st.markdown(f'<div class="metric-label">انحراف معیار</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
fig = px.histogram( | |
x=stats["histogram_data"], | |
nbins=20, | |
title=f"توزیع مقادیر {selected_layer} در مزرعه {selected_farm}", | |
labels={"x": f"مقدار {selected_layer}", "y": "فراوانی"}, | |
color_discrete_sequence=["#1a8754"] | |
) | |
fig.update_layout( | |
font=dict(family="Vazirmatn"), | |
template="plotly_white", | |
bargap=0.1 | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
st.markdown("### تحلیل زمانی") | |
dates = pd.date_range(end=selected_date, periods=30, freq='D') | |
values = np.random.normal(stats["mean"], stats["std_dev"] / 2, 30) | |
values = np.clip(values, stats["min"], stats["max"]) | |
fig = px.line( | |
x=dates, | |
y=values, | |
title=f"روند تغییرات {selected_layer} در 30 روز گذشته", | |
labels={"x": "تاریخ", "y": f"مقدار {selected_layer}"}, | |
markers=True | |
) | |
fig.update_layout( | |
font=dict(family="Vazirmatn"), | |
template="plotly_white", | |
hovermode="x unified" | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
st.markdown("### تخمین نیاز آبی") | |
water_requirement = estimate_water_requirement(selected_farm, selected_date.strftime('%Y-%m-%d')) | |
if water_requirement is not None: | |
st.metric("نیاز آبی (mm/day)", f"{water_requirement:.2f}") | |
st.info(f"نیاز آبی تخمینی برای مزرعه {selected_farm}: {water_requirement:.2f} میلیمتر در روز") | |
else: | |
st.warning("دادههای هواشناسی در دسترس نیست.") | |
if selected_layer == "SoilMoisture": | |
st.markdown("### پیشنهادات مدیریت آب") | |
if stats["mean"] < -20: | |
st.markdown("- **افزایش آبیاری**: رطوبت خاک بسیار پایین است.") | |
elif stats["mean"] > -10: | |
st.markdown("- **کاهش آبیاری**: رطوبت خاک بیش از حد است.") | |
else: | |
st.markdown("- **مدیریت بهینه**: رطوبت خاک در محدوده مناسب است.") | |
else: | |
st.warning("لطفاً ابتدا یک نقشه تولید کنید.") | |
# Data Entry Page | |
elif selected == "ورود اطلاعات": | |
st.markdown("## ورود اطلاعات روزانه مزارع") | |
tab1, tab2 = st.tabs(["ورود دستی", "آپلود فایل"]) | |
with tab1: | |
col1, col2 = st.columns(2) | |
with col1: | |
selected_week = st.selectbox( | |
"انتخاب هفته", | |
options=[str(i) for i in range(1, 23)], | |
format_func=lambda x: f"هفته {x}" | |
) | |
with col2: | |
selected_day = st.selectbox( | |
"انتخاب روز", | |
options=["شنبه", "یکشنبه", "دوشنبه", "سهشنبه", "چهارشنبه", "پنجشنبه"], | |
format_func=lambda x: x, | |
help="روز مورد نظر را برای فیلتر کردن مزارع انتخاب کنید" | |
) | |
filtered_farms = farm_df[farm_df['روز'] == selected_day] | |
if filtered_farms.empty: | |
st.warning(f"هیچ مزرعهای برای روز {selected_day} در پایگاه داده وجود ندارد.") | |
else: | |
st.markdown("### ورود دادههای مزارع") | |
data_key = f"data_{selected_week}_{selected_day}" | |
if data_key not in st.session_state: | |
st.session_state[data_key] = pd.DataFrame({ | |
'مزرعه': filtered_farms['مزرعه'], | |
'ایستگاه 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) | |
}) | |
edited_df = st.data_editor( | |
st.session_state[data_key], | |
use_container_width=True, | |
num_rows="fixed", | |
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), | |
}, | |
hide_index=True | |
) | |
for i in range(len(edited_df)): | |
stations = [ | |
edited_df.iloc[i]['ایستگاه 1'], | |
edited_df.iloc[i]['ایستگاه 2'], | |
edited_df.iloc[i]['ایستگاه 3'], | |
edited_df.iloc[i]['ایستگاه 4'], | |
edited_df.iloc[i]['ایستگاه 5'] | |
] | |
valid_stations = [s for s in stations if s > 0] | |
if valid_stations: | |
edited_df.iloc[i, edited_df.columns.get_loc('میانگین ارتفاع')] = round(sum(valid_stations) / len(valid_stations), 1) | |
st.session_state[data_key] = edited_df | |
if st.button("ذخیره اطلاعات", type="primary", use_container_width=True): | |
new_data = edited_df.copy() | |
new_data['Farm_ID'] = new_data['مزرعه'] | |
new_data['Week'] = int(selected_week) | |
new_data['Measurement_Date'] = (datetime.now() - timedelta(weeks=(22 - int(selected_week)))).strftime('%Y-%m-%d') | |
new_data['Height'] = new_data['میانگین ارتفاع'] | |
new_data['Station1'] = new_data['ایستگاه 1'] | |
new_data['Station2'] = new_data['ایستگاه 2'] | |
new_data['Station3'] = new_data['ایستگاه 3'] | |
new_data['Station4'] = new_data['ایستگاه 4'] | |
new_data['Station5'] = new_data['ایستگاه 5'] | |
new_data['Groundwater1'] = new_data['چاهک 1'] | |
new_data['Groundwater2'] = new_data['چاهک 2'] | |
new_data['Sheath_Moisture'] = new_data['رطوبت غلاف'] | |
new_data['Nitrogen'] = new_data['نیتروژن'] | |
new_data = new_data.merge( | |
farm_df[['مزرعه', 'واریته', 'سن', 'مساحت', 'کانال', 'اداره']], | |
left_on='Farm_ID', | |
right_on='مزرعه', | |
how='left' | |
) | |
new_data = new_data.rename(columns={ | |
'واریته': 'Variety', | |
'سن': 'Age', | |
'مساحت': 'Area', | |
'کانال': 'Channel', | |
'اداره': 'Administration' | |
}) | |
st.session_state.heights_df = pd.concat([st.session_state.heights_df, new_data], ignore_index=True) | |
st.success(f"دادههای هفته {selected_week} برای روز {selected_day} با موفقیت ذخیره شدند.") | |
st.balloons() | |
with tab2: | |
st.markdown("### آپلود فایل اکسل") | |
uploaded_file = st.file_uploader("فایل اکسل خود را آپلود کنید", type=["xlsx", "xls", "csv"]) | |
if uploaded_file is not None: | |
try: | |
if uploaded_file.name.endswith('.csv'): | |
df = pd.read_csv(uploaded_file) | |
else: | |
df = pd.read_excel(uploaded_file) | |
st.dataframe(df, use_container_width=True) | |
if st.button("ذخیره فایل", type="primary"): | |
st.success("فایل با موفقیت ذخیره شد.") | |
st.balloons() | |
except Exception as e: | |
st.error(f"خطا در خواندن فایل: {e}") | |
st.markdown("### راهنمای فرمت فایل") | |
st.markdown(""" | |
فایل اکسل باید شامل ستونهای زیر باشد: | |
- مزرعه | |
- ایستگاه 1 تا 5 | |
- چاهک 1 و 2 | |
- رطوبت غلاف | |
- نیتروژن | |
میتوانید از [این فایل نمونه](https://example.com/sample.xlsx) به عنوان الگو استفاده کنید. | |
""") | |
st.markdown(""" | |
<div style="border: 2px dashed #1a8754; border-radius: 10px; padding: 40px; text-align: center; margin: 20px 0;"> | |
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#1a8754" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> | |
<polyline points="17 8 12 3 7 8"></polyline> | |
<line x1="12" y1="3" x2="12" y2="15"></line> | |
</svg> | |
<p style="margin-top: 10px; color: #1a8754;">فایل خود را اینجا رها کنید یا روی دکمه بالا کلیک کنید</p> | |
</div> | |
""", unsafe_allow_html=True) | |
# Data Analysis Page | |
elif selected == "تحلیل دادهها": | |
st.markdown("## تحلیل هوشمند دادهها") | |
col1, col2 = st.columns([1, 2]) | |
with col1: | |
st_lottie(lottie_analysis, height=200, key="analysis_animation") | |
with col2: | |
st.markdown(""" | |
<div class="glass-card"> | |
<h3 class="gradient-text">تحلیل پیشرفته دادههای مزارع</h3> | |
<p>در این بخش میتوانید تحلیلهای پیشرفته روی دادههای مزارع انجام دهید و روندها و الگوهای مختلف را بررسی کنید.</p> | |
</div> | |
""", unsafe_allow_html=True) | |
tab1, tab2, tab3, tab4 = st.tabs(["تحلیل رشد", "مقایسه واریتهها", "تحلیل شاخصها", "پیشبینی"]) | |
with tab1: | |
st.markdown("### تحلیل رشد مزارع") | |
col1, col2 = st.columns(2) | |
with col1: | |
selected_variety = st.selectbox( | |
"انتخاب واریته", | |
["all"] + list(farm_df['واریته'].unique()), | |
format_func=lambda x: "همه واریتهها" if x == "all" else x, | |
key="growth_variety" | |
) | |
with col2: | |
selected_age = st.selectbox( | |
"انتخاب سن", | |
["all"] + list(farm_df['سن'].unique()), | |
format_func=lambda x: "همه سنین" if x == "all" else x, | |
key="growth_age" | |
) | |
growth_data = generate_mock_growth_data(farm_df, selected_variety, selected_age) | |
if growth_data['individual']: | |
chart_data = [] | |
for farm_data in growth_data['individual']: | |
for i, week in enumerate(farm_data['weeks']): | |
chart_data.append({ | |
'Farm': farm_data['farm_id'], | |
'Week': week, | |
'Height': farm_data['heights'][i], | |
'Variety': farm_data['variety'], | |
'Age': farm_data['age'] | |
}) | |
chart_df = pd.DataFrame(chart_data) | |
chart = alt.Chart(chart_df).mark_line(point=True).encode( | |
x=alt.X('Week:Q', title='هفته'), | |
y=alt.Y('Height:Q', title='ارتفاع (سانتیمتر)'), | |
color=alt.Color('Farm:N', title='مزرعه'), | |
tooltip=['Farm', 'Week', 'Height', 'Variety', 'Age'] | |
).properties( | |
width='container', | |
height=400, | |
title='روند رشد مزارع بر اساس هفته' | |
).interactive() | |
st.altair_chart(chart, use_container_width=True) | |
st.markdown("### تحلیل نرخ رشد") | |
growth_rates = [] | |
for farm_data in growth_data['individual']: | |
heights = farm_data['heights'] | |
for i in range(1, len(heights)): | |
growth_rate = heights[i] - heights[i-1] | |
growth_rates.append({ | |
'Farm': farm_data['farm_id'], | |
'Week': farm_data['weeks'][i], | |
'Growth Rate': growth_rate, | |
'Variety': farm_data['variety'], | |
'Age': farm_data['age'] | |
}) | |
growth_rate_df = pd.DataFrame(growth_rates) | |
chart = alt.Chart(growth_rate_df).mark_bar().encode( | |
x=alt.X('Week:O', title='هفته'), | |
y=alt.Y('mean(Growth Rate):Q', title='نرخ رشد (سانتیمتر در هفته)'), | |
color=alt.Color('Farm:N', title='مزرعه'), | |
tooltip=['Farm', 'Week', 'mean(Growth Rate)'] | |
).properties( | |
width='container', | |
height=400, | |
title='نرخ رشد هفتگی مزارع' | |
).interactive() | |
st.altair_chart(chart, use_container_width=True) | |
else: | |
st.warning("دادهای برای نمایش وجود ندارد.") | |
with tab2: | |
st.markdown("### مقایسه واریتهها") | |
variety_age_groups = farm_df.groupby(['واریته', 'سن']).size().reset_index(name='تعداد') | |
fig = px.density_heatmap( | |
variety_age_groups, | |
x='واریته', | |
y='سن', | |
z='تعداد', | |
title='توزیع مزارع بر اساس واریته و سن', | |
color_continuous_scale='Viridis' | |
) | |
fig.update_layout( | |
font=dict(family="Vazirmatn"), | |
template="plotly_white", | |
xaxis_title="واریته", | |
yaxis_title="سن" | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
varieties = farm_df['واریته'].unique() | |
variety_heights = {variety: np.random.normal(150, 20, 100) for variety in varieties} | |
fig = go.Figure() | |
for variety in varieties: | |
fig.add_trace(go.Box( | |
y=variety_heights[variety], | |
name=variety, | |
boxpoints='outliers', | |
marker_color=f'hsl({hash(variety) % 360}, 70%, 50%)' | |
)) | |
fig.update_layout( | |
title='مقایسه ارتفاع بر اساس واریته', | |
yaxis_title='ارتفاع (سانتیمتر)', | |
font=dict(family="Vazirmatn"), | |
template="plotly_white", | |
boxmode='group' | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
st.markdown("### مقایسه آماری واریتهها") | |
variety_stats = {} | |
for variety in varieties: | |
heights = variety_heights[variety] | |
variety_stats[variety] = { | |
'میانگین': np.mean(heights), | |
'میانه': np.median(heights), | |
'انحراف معیار': np.std(heights), | |
'حداقل': np.min(heights), | |
'حداکثر': np.max(heights) | |
} | |
variety_stats_df = pd.DataFrame(variety_stats).T | |
st.dataframe(variety_stats_df, use_container_width=True) | |
with tab3: | |
st.markdown("### تحلیل شاخصهای ماهوارهای") | |
selected_day = st.selectbox( | |
"انتخاب روز برای تحلیل", | |
options=["شنبه", "یکشنبه", "دوشنبه", "سهشنبه", "چهارشنبه", "پنجشنبه"], | |
format_func=lambda x: x | |
) | |
filtered_farms = farm_df[farm_df['روز'] == selected_day] | |
if filtered_farms.empty: | |
st.warning(f"هیچ مزرعهای برای روز {selected_day} یافت نشد.") | |
else: | |
indices_data = [] | |
with st.spinner('در حال محاسبه شاخصها از Google Earth Engine...'): | |
for _, farm in filtered_farms.iterrows(): | |
farm_id = farm['مزرعه'] | |
indices = calculate_indices(farm_id, datetime.now().strftime('%Y-%m-%d')) | |
indices_data.append({ | |
'مزرعه': farm_id, | |
'NDVI': indices['NDVI'], | |
'NDWI': indices['NDWI'], | |
'LAI': indices['LAI'], | |
'CHL': indices['CHL'] | |
}) | |
indices_df = pd.DataFrame(indices_data) | |
indices_df = indices_df.sort_values(by='NDVI', ascending=False) | |
st.markdown("### جدول شاخصها") | |
st.dataframe(indices_df, use_container_width=True) | |
st.markdown("### رتبهبندی مزارع بر اساس NDVI") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.markdown("#### بهترین مزارع") | |
st.dataframe(indices_df.head(5)[['مزرعه', 'NDVI']], use_container_width=True) | |
with col2: | |
st.markdown("#### بدترین مزارع") | |
st.dataframe(indices_df.tail(5)[['مزرعه', 'NDVI']], use_container_width=True) | |
st.markdown("### مزارع دارای تنش (NDVI < 0.3)") | |
stressed_farms = indices_df[indices_df['NDVI'] < 0.3] | |
if not stressed_farms.empty: | |
st.dataframe(stressed_farms[['مزرعه', 'NDVI']], use_container_width=True) | |
else: | |
st.success("هیچ مزرعهای با تنش (NDVI < 0.3) یافت نشد.") | |
st.markdown("### نمودار NDVI مزارع") | |
fig = px.bar( | |
indices_df, | |
x='مزرعه', | |
y='NDVI', | |
title=f'شاخص NDVI مزارع برای روز {selected_day}', | |
color='NDVI', | |
color_continuous_scale=['#ff0000', '#ffd700', '#32cd32'], | |
range_color=[0, 1] | |
) | |
fig.update_layout( | |
font=dict(family="Vazirmatn"), | |
template="plotly_white", | |
height=500 | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
with tab4: | |
st.markdown("### پیشبینی رشد مزارع") | |
selected_farm_for_prediction = st.selectbox( | |
"انتخاب مزرعه", | |
options=farm_df['مزرعه'].tolist(), | |
format_func=lambda x: f"مزرعه {x}" | |
) | |
weeks = list(range(1, 16)) | |
heights = [50 + i * 10 + np.random.normal(0, 5) for i in range(len(weeks))] | |
historical_df = pd.DataFrame({ | |
'Week': weeks, | |
'Height': heights | |
}) | |
future_weeks = list(range(16, 23)) | |
from sklearn.linear_model import LinearRegression | |
model = LinearRegression() | |
model.fit(np.array(weeks).reshape(-1, 1), heights) | |
future_heights = model.predict(np.array(future_weeks).reshape(-1, 1)) | |
lower_bound = future_heights - 15 | |
upper_bound = future_heights + 15 | |
future_df = pd.DataFrame({ | |
'Week': future_weeks, | |
'Height': future_heights, | |
'Lower': lower_bound, | |
'Upper': upper_bound | |
}) | |
fig = go.Figure() | |
fig.add_trace(go.Scatter( | |
x=historical_df['Week'], | |
y=historical_df['Height'], | |
mode='lines+markers', | |
name='دادههای تاریخی', | |
line=dict(color='#1a8754', width=3), | |
marker=dict(size=8, color='#1a8754') | |
)) | |
fig.add_trace(go.Scatter( | |
x=future_df['Week'], | |
y=future_df['Height'], | |
mode='lines+markers', | |
name='پیشبینی', | |
line=dict(color='#ff9800', width=3, dash='dash'), | |
marker=dict(size=8, color='#ff9800') | |
)) | |
fig.add_trace(go.Scatter( | |
x=future_df['Week'].tolist() + future_df['Week'].tolist()[::-1], | |
y=future_df['Upper'].tolist() + future_df['Lower'].tolist()[::-1], | |
fill='toself', | |
fillcolor='rgba(255, 152, 0, 0.2)', | |
line=dict(color='rgba(255, 152, 0, 0)'), | |
hoverinfo='skip', | |
showlegend=False | |
)) | |
fig.update_layout( | |
title=f'پیشبینی رشد مزرعه {selected_farm_for_prediction}', | |
xaxis_title='هفته', | |
yaxis_title='ارتفاع (سانتیمتر)', | |
font=dict(family='Vazirmatn', size=14), | |
hovermode='x unified', | |
template='plotly_white', | |
height=500, | |
legend=dict( | |
orientation="h", | |
yanchor="bottom", | |
y=1.02, | |
xanchor="right", | |
x=1 | |
) | |
) | |
fig.add_vline(x=15.5, line_width=1, line_dash="dash", line_color="gray") | |
st.plotly_chart(fig, use_container_width=True) | |
st.markdown("### جزئیات پیشبینی") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.metric( | |
label="ارتفاع فعلی", | |
value=f"{heights[-1]:.1f} cm", | |
delta=f"{heights[-1] - heights[-2]:.1f} cm" | |
) | |
with col2: | |
st.metric( | |
label="ارتفاع پیشبینی شده (هفته 22)", | |
value=f"{future_heights[-1]:.1f} cm", | |
delta=f"{future_heights[-1] - heights[-1]:.1f} cm" | |
) | |
prediction_table = pd.DataFrame({ | |
'هفته': future_weeks, | |
'ارتفاع پیشبینی شده': [f"{h:.1f}" for h in future_heights], | |
'حد پایین': [f"{l:.1f}" for l in lower_bound], | |
'حد بالا': [f"{u:.1f}" for u in upper_bound] | |
}) | |
st.dataframe(prediction_table, use_container_width=True, hide_index=True) | |
st.markdown("### عوامل مؤثر بر رشد") | |
factors = ['رطوبت', 'نیتروژن', 'دما', 'آبیاری', 'نور'] | |
factor_values = [85, 70, 60, 90, 75] | |
fig = go.Figure() | |
fig.add_trace(go.Scatterpolar( | |
r=factor_values, | |
theta=factors, | |
fill='toself', | |
name='عوامل مؤثر', | |
line_color='#1a8754' | |
)) | |
fig.update_layout( | |
polar=dict( | |
radialaxis=dict( | |
visible=True, | |
range=[0, 100] | |
) | |
), | |
showlegend=False, | |
font=dict(family='Vazirmatn'), | |
height=400 | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
# Reporting Page | |
elif selected == "گزارشگیری": | |
st.markdown("## گزارشگیری پیشرفته") | |
col1, col2 = st.columns([1, 2]) | |
with col1: | |
st_lottie(lottie_report, height=200, key="report_animation") | |
with col2: | |
st.markdown(""" | |
<div class="glass-card"> | |
<h3 class="gradient-text">گزارشگیری پیشرفته</h3> | |
<p>در این بخش میتوانید گزارشهای مختلف از وضعیت مزارع تهیه کنید و آنها را به صورت PDF یا Excel دانلود کنید.</p> | |
</div> | |
""", unsafe_allow_html=True) | |
col1, col2 = st.columns(2) | |
with col1: | |
start_date = st.date_input( | |
"تاریخ شروع", | |
value=datetime.now() - timedelta(days=30), | |
format="YYYY-MM-DD" | |
) | |
with col2: | |
end_date = st.date_input( | |
"تاریخ پایان", | |
value=datetime.now(), | |
format="YYYY-MM-DD" | |
) | |
report_type = st.selectbox( | |
"نوع گزارش", | |
options=["گزارش کلی", "گزارش رشد", "گزارش رطوبت", "گزارش مقایسهای واریتهها"] | |
) | |
if st.button("تولید گزارش", type="primary", use_container_width=True): | |
with st.spinner('در حال تولید گزارش...'): | |
time.sleep(2) | |
if report_type == "گزارش کلی": | |
st.markdown("### گزارش کلی وضعیت مزارع") | |
col1, col2, col3, col4 = st.columns(4) | |
with col1: | |
st.metric("تعداد کل مزارع", len(farm_df)) | |
with col2: | |
st.metric("میانگین ارتفاع", f"{np.random.uniform(150, 200):.1f} cm") | |
with col3: | |
st.metric("میانگین رطوبت", f"{np.random.uniform(60, 80):.1f}%") | |
with col4: | |
st.metric("میانگین نیتروژن", f"{np.random.uniform(40, 60):.1f}%") | |
farm_counts = farm_df['اداره'].value_counts() | |
fig = px.pie( | |
values=farm_counts.values, | |
names=farm_counts.index, | |
title='توزیع مزارع بر اساس اداره', | |
color_discrete_sequence=px.colors.sequential.Greens_r | |
) | |
fig.update_traces(textposition='inside', textinfo='percent+label') | |
fig.update_layout(font=dict(family="Vazirmatn")) | |
st.plotly_chart(fig, use_container_width=True) | |
weeks = list(range(1, 23)) | |
heights = [100 + i * 5 + np.random.normal(0, 10) for i in range(len(weeks))] | |
fig = px.line( | |
x=weeks, | |
y=heights, | |
title='روند رشد کلی مزارع', | |
labels={'x': 'هفته', 'y': 'ارتفاع (سانتیمتر)'} | |
) | |
fig.update_layout(font=dict(family="Vazirmatn")) | |
st.plotly_chart(fig, use_container_width=True) | |
top_farms = pd.DataFrame({ | |
'مزرعه': ['مزرعه ' + str(i) for i in range(1, 6)], | |
'ارتفاع': [round(np.random.uniform(180, 220), 1) for _ in range(5)], | |
'رطوبت': [round(np.random.uniform(60, 80), 1) for _ in range(5)], | |
'نیتروژن': [round(np.random.uniform(40, 60), 1) for _ in range(5)] | |
}) | |
st.markdown("### 5 مزرعه برتر") | |
st.dataframe(top_farms, use_container_width=True, hide_index=True) | |
elif report_type == "گزارش رشد": | |
st.markdown("### گزارش رشد مزارع") | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
st.metric("میانگین رشد هفتگی", f"{np.random.uniform(10, 15):.1f} cm") | |
with col2: | |
st.metric("حداکثر رشد هفتگی", f"{np.random.uniform(20, 25):.1f} cm") | |
with col3: | |
st.metric("حداقل رشد هفتگی", f"{np.random.uniform(5, 10):.1f} cm") | |
weeks = list(range(1, 23)) | |
farms = ['مزرعه 1', 'مزرعه 2', 'مزرعه 3', 'مزرعه 4', 'مزرعه 5'] | |
fig = go.Figure() | |
for farm in farms: | |
heights = [100 + i * 5 + np.random.normal(0, 10) for i in range(len(weeks))] | |
fig.add_trace(go.Scatter( | |
x=weeks, | |
y=heights, | |
mode='lines+markers', | |
name=farm | |
)) | |
fig.update_layout( | |
title='روند رشد مزارع', | |
xaxis_title='هفته', | |
yaxis_title='ارتفاع (سانتیمتر)', | |
font=dict(family="Vazirmatn"), | |
legend_title='مزرعه', | |
hovermode="x unified" | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
growth_rates = np.random.normal(12, 3, 1000) | |
fig = px.histogram( | |
x=growth_rates, | |
nbins=30, | |
title='توزیع نرخ رشد هفتگی', | |
labels={'x': 'نرخ رشد (سانتیمتر در هفته)', 'y': 'فراوانی'}, | |
color_discrete_sequence=['#1a8754'] | |
) | |
fig.update_layout(font=dict(family="Vazirmatn")) | |
st.plotly_chart(fig, use_container_width=True) | |
st.markdown("### عوامل مؤثر بر رشد") | |
factors = ['رطوبت', 'نیتروژن', 'دما', 'آبیاری', 'نور'] | |
correlations = [0.8, 0.7, 0.5, 0.9, 0.6] | |
fig = px.bar( | |
x=factors, | |
y=correlations, | |
title='همبستگی عوامل مختلف با نرخ رشد', | |
labels={'x': 'عامل', 'y': 'ضریب همبستگی'}, | |
color=correlations, | |
color_continuous_scale='Viridis' | |
) | |
fig.update_layout(font=dict(family="Vazirmatn")) | |
st.plotly_chart(fig, use_container_width=True) | |
elif report_type == "گزارش رطوبت": | |
st.markdown("### گزارش رطوبت مزارع") | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
st.metric("میانگین رطوبت", f"{np.random.uniform(60, 70):.1f}%") | |
with col2: | |
st.metric("حداکثر رطوبت", f"{np.random.uniform(80, 90):.1f}%") | |
with col3: | |
st.metric("حداقل رطوبت", f"{np.random.uniform(40, 50):.1f}%") | |
dates = pd.date_range(start=start_date, end=end_date) | |
moisture_levels = [np.random.uniform(50, 80) for _ in range(len(dates))] | |
fig = px.line( | |
x=dates, | |
y=moisture_levels, | |
title='روند رطوبت مزارع', | |
labels={'x': 'تاریخ', 'y': 'رطوبت (%)'} | |
) | |
fig.update_layout(font=dict(family="Vazirmatn")) | |
st.plotly_chart(fig, use_container_width=True) | |
fig = px.histogram( | |
x=moisture_levels, | |
nbins=30, | |
title='توزیع رطوبت مزارع', | |
labels={'x': 'رطوبت (%)', 'y': 'فراوانی'}, | |
color_discrete_sequence=['#1a8754'] | |
) | |
fig.update_layout(font=dict(family="Vazirmatn")) | |
st.plotly_chart(fig, use_container_width=True) | |
growth_levels = [h + np.random.normal(0, 10) for h in moisture_levels] | |
fig = px.scatter( | |
x=moisture_levels, | |
y=growth_levels, | |
title='رابطه بین رطوبت و رشد', | |
labels={'x': 'رطوبت (%)', 'y': 'رشد (سانتیمتر)'}, | |
trendline='ols' | |
) | |
fig.update_layout(font=dict(family="Vazirmatn")) | |
st.plotly_chart(fig, use_container_width=True) | |
st.markdown("### توصیههای مدیریت رطوبت") | |
recommendations = [ | |
"افزایش دفعات آبیاری در مزارع با رطوبت پایین", | |
"بهبود سیستم زهکشی در مزارع با رطوبت بالا", | |
"استفاده از مالچ برای حفظ رطوبت خاک", | |
"تنظیم زمان آبیاری بر اساس شرایط آب و هوایی", | |
"پایش مداوم رطوبت خاک با استفاده از سنسورها" | |
] | |
for rec in recommendations: | |
st.markdown(f"- {rec}") | |
elif report_type == "گزارش مقایسهای واریتهها": | |
st.markdown("### گزارش مقایسهای واریتههای نیشکر") | |
varieties = ['CP57-614', 'CP69-1062', 'CP73-21', 'SP70-1143', 'IRC99-02'] | |
heights = [np.random.uniform(180, 220) for _ in varieties] | |
sugar_contents = [np.random.uniform(12, 16) for _ in varieties] | |
growth_rates = [np.random.uniform(10, 15) for _ in varieties] | |
fig = go.Figure(data=[ | |
go.Bar(name='ارتفاع (cm)', x=varieties, y=heights), | |
go.Bar(name='محتوای قند (%)', x=varieties, y=sugar_contents), | |
go.Bar(name='رشد (cm/هفته)', x=varieties, y=growth_rates) | |
]) | |
fig.update_layout( | |
title='مقایسه واریتههای نیشکر', | |
xaxis_title='واریته', | |
yaxis_title='مقدار', | |
barmode='group', | |
font=dict(family="Vazirmatn") | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
variety_data = pd.DataFrame({ | |
'واریته': varieties, | |
'ارتفاع (cm)': [round(h, 1) for h in heights], | |
'محتوای قند (%)': [round(s, 1) for s in sugar_contents], | |
'رشد(cm/هفته)': [round(g, 1) for g in growth_rates], | |
'مقاومت به آفات': [np.random.choice(['کم', 'متوسط', 'زیاد']) for _ in varieties], | |
'نیاز آبی': [np.random.choice(['کم', 'متوسط', 'زیاد']) for _ in varieties] | |
}) | |
st.dataframe(variety_data, use_container_width=True, hide_index=True) | |
categories = ['ارتفاع', 'محتوای قند', 'رشد', 'مقاومت به آفات', 'بهرهوری آب'] | |
fig = go.Figure() | |
for variety in varieties: | |
values = [ | |
heights[varieties.index(variety)] / max(heights) * 100, | |
sugar_contents[varieties.index(variety)] / max(sugar_contents) * 100, | |
growth_rates[varieties.index(variety)] / max(growth_rates) * 100, | |
np.random.uniform(60, 100), | |
np.random.uniform(60, 100) | |
] | |
fig.add_trace(go.Scatterpolar( | |
r=values, | |
theta=categories, | |
fill='toself', | |
name=variety | |
)) | |
fig.update_layout( | |
polar=dict( | |
radialaxis=dict( | |
visible=True, | |
range=[0, 100] | |
) | |
), | |
title='مقایسه واریتهها', | |
font=dict(family="Vazirmatn"), | |
showlegend=True | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
st.markdown("### توصیههای کشت واریتهها") | |
recommendations = [ | |
f"واریته {np.random.choice(varieties)} برای مناطق با آب و هوای گرم و مرطوب مناسبتر است.", | |
f"برای افزایش عملکرد تولید شکر، کشت واریته {np.random.choice(varieties)} توصیه میشود.", | |
f"در مناطق با محدودیت آب، استفاده از واریته {np.random.choice(varieties)} به دلیل نیاز آبی کمتر مناسب است.", | |
f"برای مقاومت بهتر در برابر آفات، واریته {np.random.choice(varieties)} پیشنهاد میشود.", | |
"تنوع در کشت واریتهها میتواند به کاهش ریسکهای مرتبط با آفات و بیماریها کمک کند." | |
] | |
for rec in recommendations: | |
st.markdown(f"- {rec}") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.download_button( | |
label="دانلود گزارش (PDF)", | |
data=b"This is a mock PDF report", | |
file_name="farm_report.pdf", | |
mime="application/pdf", | |
) | |
with col2: | |
st.download_button( | |
label="دانلود دادهها (Excel)", | |
data=b"This is a mock Excel file", | |
file_name="farm_data.xlsx", | |
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
) | |
# Settings Page | |
elif selected == "تنظیمات": | |
st.markdown("## تنظیمات سیستم") | |
tab1, tab2, tab3, tab4 = st.tabs(["تنظیمات کاربری", "تنظیمات سیستم", "مدیریت دادهها", "پشتیبانگیری"]) | |
with tab1: | |
st.markdown("### تنظیمات کاربری") | |
st.markdown("#### پروفایل کاربری") | |
col1, col2 = st.columns(2) | |
with col1: | |
user_name = st.text_input("نام کاربری", value="کاربر نمونه") | |
with col2: | |
user_email = st.text_input("ایمیل", value="user@example.com") | |
user_role = st.selectbox( | |
"نقش کاربری", | |
options=["مدیریت مطالعات", "پرسنل", "اپراتور"], | |
index=1 | |
) | |
st.markdown("#### تغییر رمز عبور") | |
col1, col2 = st.columns(2) | |
with col1: | |
current_password = st.text_input("رمز عبور فعلی", type="password") | |
with col2: | |
new_password = st.text_input("رمز عبور جدید", type="password") | |
confirm_password = st.text_input("تکرار رمز عبور جدید", type="password") | |
if st.button("تغییر رمز عبور", type="primary"): | |
st.success("رمز عبور با موفقیت تغییر کرد.") | |
st.markdown("#### تنظیمات اعلانها") | |
email_notifications = st.checkbox("دریافت اعلانها از طریق ایمیل", value=True) | |
sms_notifications = st.checkbox("دریافت اعلانها از طریق پیامک", value=False) | |
notification_frequency = st.radio( | |
"تناوب دریافت اعلانها", | |
options=["روزانه", "هفتگی", "ماهانه"], | |
index=1 | |
) | |
with tab2: | |
st.markdown("### تنظیمات سیستم") | |
system_language = st.selectbox( | |
"زبان سیستم", | |
options=["فارسی", "English", "العربية"], | |
index=0 | |
) | |
date_format = st.selectbox( | |
"فرمت تاریخ", | |
options=["YYYY/MM/DD", "DD/MM/YYYY", "MM/DD/YYYY"], | |
index=0 | |
) | |
st.markdown("#### تنظیمات ظاهری") | |
theme = st.radio( | |
"تم", | |
options=["روشن", "تیره", "سیستم"], | |
index=2 | |
) | |
primary_color = st.color_picker("رنگ اصلی", value="#1a8754") | |
st.markdown("#### تنظیمات نقشه") | |
default_map_view = st.selectbox( | |
"نمای پیشفرض نقشه", | |
options=["نقشه", "ماهواره", "ترکیبی"], | |
index=0 | |
) | |
default_map_layer = st.selectbox( | |
"لایه پیشفرض نقشه", | |
options=["NDVI", "NDMI", "EVI", "NDWI", "SoilMoisture"], | |
index=0 | |
) | |
st.markdown("#### تنظیمات مدل هوش مصنوعی") | |
ai_model = st.selectbox( | |
"مدل هوش مصنوعی", | |
options=["GPT-3", "GPT-4", "BERT"], | |
index=1 | |
) | |
model_update_frequency = st.selectbox( | |
"تناوب بهروزرسانی مدل", | |
options=["روزانه", "هفتگی", "ماهانه"], | |
index=1 | |
) | |
if st.button("ذخیره تنظیمات", type="primary"): | |
st.success("تنظیمات با موفقیت ذخیره شدند.") | |
with tab3: | |
st.markdown("### مدیریت دادهها") | |
st.markdown("#### ورود داده") | |
uploaded_file = st.file_uploader("آپلود فایل داده", type=["csv", "xlsx"]) | |
if uploaded_file is not None: | |
st.success(f"فایل {uploaded_file.name} با موفقیت آپلود شد.") | |
if st.button("وارد کردن دادهها"): | |
st.info("در حال پردازش و وارد کردن دادهها...") | |
time.sleep(2) | |
st.success("دادهها با موفقیت وارد شدند.") | |
st.markdown("#### خروجی داده") | |
export_format = st.selectbox( | |
"فرمت خروجی", | |
options=["CSV", "Excel", "JSON"], | |
index=1 | |
) | |
if st.button("دریافت خروجی"): | |
st.info("در حال آمادهسازی فایل خروجی...") | |
time.sleep(2) | |
st.success("فایل خروجی آماده دانلود است.") | |
st.download_button( | |
label="دانلود فایل خروجی", | |
data=b"This is a mock export file", | |
file_name=f"farm_data_export.{export_format.lower()}", | |
mime="application/octet-stream", | |
) | |
st.markdown("#### پاکسازی دادهها") | |
cleanup_options = st.multiselect( | |
"گزینههای پاکسازی", | |
options=["حذف دادههای تکراری", "حذف دادههای ناقص", "نرمالسازی دادهها"], | |
default=["حذف دادههای تکراری"] | |
) | |
if st.button("اجرای پاکسازی"): | |
st.info("در حال اجرای عملیات پاکسازی...") | |
time.sleep(2) | |
st.success("عملیات پاکسازی با موفقیت انجام شد.") | |
st.markdown("#### تنظیمات نمایش داده") | |
chart_theme = st.selectbox( | |
"تم نمودارها", | |
options=["پیشفرض", "روشن", "تیره", "رنگی"], | |
index=0 | |
) | |
show_data_labels = st.checkbox("نمایش برچسبهای داده", value=True) | |
if st.button("اعمال تنظیمات نمایش"): | |
st.success("تنظیمات نمایش داده با موفقیت اعمال شدند.") | |
with tab4: | |
st.markdown("### پشتیبانگیری و بازیابی") | |
st.markdown("#### ایجاد نسخه پشتیبان") | |
backup_type = st.radio( | |
"نوع پشتیبانگیری", | |
options=["پشتیبان کامل", "پشتیبان افزایشی"], | |
index=0 | |
) | |
include_images = st.checkbox("شامل تصاویر", value=True) | |
include_user_data = st.checkbox("شامل دادههای کاربران", value=True) | |
if st.button("ایجاد نسخه پشتیبان", type="primary"): | |
st.info("در حال ایجاد نسخه پشتیبان...") | |
progress_bar = st.progress(0) | |
for i in range(100): | |
time.sleep(0.05) | |
progress_bar.progress(i + 1) | |
st.success("نسخه پشتیبان با موفقیت ایجاد شد.") | |
st.markdown("#### بازیابی از نسخه پشتیبان") | |
backup_file = st.file_uploader("آپلود فایل پشتیبان", type=["zip", "bak"]) | |
if backup_file is not None: | |
st.warning("هشدار: بازیابی از نسخه پشتیبان ممکن است دادههای فعلی را بازنویسی کند.") | |
if st.button("شروع بازیابی"): | |
st.info("در حال بازیابی از نسخه پشتیبان...") | |
progress_bar = st.progress(0) | |
for i in range(100): | |
time.sleep(0.05) | |
progress_bar.progress(i + 1) | |
st.success("بازیابی از نسخه پشتیبان با موفقیت انجام شد.") | |
st.markdown("#### تنظیمات پشتیبانگیری خودکار") | |
auto_backup = st.checkbox("فعالسازی پشتیبانگیری خودکار", value=True) | |
if auto_backup: | |
backup_frequency = st.selectbox( | |
"تناوب پشتیبانگیری", | |
options=["روزانه", "هفتگی", "ماهانه"], | |
index=1 | |
) | |
backup_time = st.time_input("زمان پشتیبانگیری", value=datetime.now().replace(hour=1, minute=0, second=0, microsecond=0)) | |
retain_backups = st.number_input("تعداد نسخههای پشتیبان برای نگهداری", min_value=1, value=7) | |
if st.button("ذخیره تنظیمات پشتیبانگیری"): | |
st.success("تنظیمات پشتیبانگیری با موفقیت ذخیره شدند.") | |
# Add a footer | |
st.markdown(""" | |
<footer style="position: fixed; left: 0; bottom: 0; width: 100%; background-color: #1a8754; color: white; text-align: center; padding: 10px 0;"> | |
<p>© سامانه هوشمند پایش مزارع نیشکر کشت و صنعت دهخدا | طراحی شده توسط تیم مطالعات کاربردی توسعه</p> | |
</footer> | |
""", unsafe_allow_html=True) |