Sugarcane / app.py
Esmaeilkianii's picture
Update app.py
fc36b20 verified
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
@st.cache_resource
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
@st.cache_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()
@st.cache_data
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
@st.cache_data
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
@st.cache_data
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)