diff --git "a/app.py" "b/app.py"
--- "a/app.py"
+++ "b/app.py"
@@ -1,588 +1,2182 @@
-import streamlit as st
+import streamlit
import pandas as pd
import numpy as np
-import matplotlib.pyplot as plt
-import plotly.express as px
-import plotly.graph_objects as go
-import folium
-from folium.plugins import HeatMap
+import folium#
from streamlit_folium import folium_static
-import json
-import os
import ee
-import geemap.foliumap as geemap
+import os
+import json
+import time
from datetime import datetime, timedelta
-import io
+import plotly.express as px
+import plotly.graph_objects as go
+from PIL import Image
import base64
-from reportlab.pdfgen import canvas
-from reportlab.lib.pagesizes import letter
-from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
-from reportlab.lib import colors
-from sklearn.ensemble import RandomForestRegressor
+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 seaborn as sns
-from PIL import Image
-import streamlit_authenticator as stauth
-import pickle
-from pathlib import Path
-import time
-import statsmodels.api as sm
-from statsmodels.tsa.arima.model import ARIMA
+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="🌱",
+ page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا",
+ page_icon="🌿",
layout="wide",
initial_sidebar_state="expanded"
)
-# تنظیمات CSS سفارشی
-with open("style.css") as f:
- st.markdown(f'', unsafe_allow_html=True)
-
-# تنظیمات فونت فارسی
-st.markdown(
- """
-
- """,
- unsafe_allow_html=True
-)
-
-# توابع برای لود انیمیشنهای Lottie
-def load_lottieurl(url):
- r = requests.get(url)
- if r.status_code != 200:
- return None
- return r.json()
-
-# بارگذاری انیمیشنهای Lottie
-lottie_farm = load_lottieurl("https://assets4.lottiefiles.com/packages/lf20_qbkoyp6j.json")
-lottie_analytics = load_lottieurl("https://assets2.lottiefiles.com/private_files/lf30_ajzyv37m.json")
-lottie_report = load_lottieurl("https://assets9.lottiefiles.com/packages/lf20_vfcu9yyj.json")
+
+ ::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+ }
+
+ ::-webkit-scrollbar-track {
+ background: #f1f1f1;
+ border-radius: 10px;
+ }
+
+ ::-webkit-scrollbar-thumb {
+ background: #1a8754;
+ border-radius: 10px;
+ }
+
+ ::-webkit-scrollbar-thumb:hover {
+ background: #115740;
+ }
+
+ .tooltip {
+ position: relative;
+ display: inline-block;
+ }
+
+ .tooltip .tooltiptext {
+ visibility: hidden;
+ width: 120px;
+ background-color: #555;
+ color: #fff;
+ text-align: center;
+ border-radius: 6px;
+ padding: 5px;
+ position: absolute;
+ z-index: 1;
+ bottom: 125%;
+ left: 50%;
+ margin-left: -60px;
+ opacity: 0;
+ transition: opacity 0.3s;
+ }
+
+ .tooltip:hover .tooltiptext {
+ visibility: visible;
+ opacity: 1;
+ }
+
+ .dataframe {
+ border-collapse: collapse;
+ width: 100%;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+ }
+
+ .dataframe th {
+ background-color: #1a8754;
+ color: white;
+ padding: 12px;
+ text-align: right;
+ }
+
+ .dataframe td {
+ padding: 10px 12px;
+ border-bottom: 1px solid #e9ecef;
+ }
+
+ .dataframe tr:nth-child(even) {
+ background-color: #f8f9fa;
+ }
+
+ .dataframe tr:hover {
+ background-color: #e9ecef;
+ }
+
+ .stProgress > div > div > div > div {
+ background-color: #1a8754;
+ }
+
+ .notification {
+ background-color: #d1e7dd;
+ color: #0f5132;
+ padding: 1rem;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+ display: flex;
+ align-items: center;
+ animation: slideIn 0.5s ease;
+ }
+
+ @keyframes slideIn {
+ 0% { transform: translateX(100%); opacity: 0; }
+ 100% { transform: translateX(0); opacity: 1; }
+ }
+
+ .notification-icon {
+ margin-right: 0.5rem;
+ font-size: 1.2rem;
+ }
+
+ .custom-select {
+ background-color: white;
+ border-radius: 8px;
+ padding: 0.5rem;
+ border: 1px solid #ced4da;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
+ }
+
+ .glass-card {
+ background: rgba(255, 255, 255, 0.7);
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+ border-radius: 12px;
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ padding: 1.5rem;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ }
+
+ .neumorphic-card {
+ background: #f0f0f3;
+ border-radius: 12px;
+ box-shadow: 10px 10px 20px #d1d1d4, -10px -10px 20px #ffffff;
+ padding: 1.5rem;
+ }
+
+ .gradient-text {
+ background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ font-weight: 700;
+ }
+
+ @keyframes pulse {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(1.05); }
+ 100% { transform: scale(1); }
+ }
+
+ .pulse-animation {
+ animation: pulse 2s infinite;
+ }
+
+ .stRadio > div {
+ display: flex;
+ gap: 10px;
+ }
+
+ .stRadio label {
+ cursor: pointer;
+ background-color: #f8f9fa;
+ padding: 0.5rem 1rem;
+ border-radius: 50px;
+ transition: all 0.3s ease;
+ }
+
+ .stRadio label:hover {
+ background-color: #e9ecef;
+ }
+
+ .stRadio input {
+ display: none;
+ }
+
+ .stRadio input:checked + label {
+ background-color: #1a8754;
+ color: white;
+ }
+
+ .stSelectbox, .stNumberInput {
+ background-color: #f0f2f6;
+ border-radius: 10px;
+ padding: 10px;
+ margin: 10px 0;
+ }
+
+ .custom-card {
+ background-color: white;
+ padding: 20px;
+ border-radius: 15px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+ margin: 10px 0;
+ }
+
+ .metric-container {
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ }
+
+ .metric-card {
+ background-color: #1a8754;
+ color: white;
+ padding: 15px;
+ border-radius: 10px;
+ margin: 5px;
+ flex: 1;
+ min-width: 200px;
+ text-align: center;
+ }
+
+""", unsafe_allow_html=True)
-# اتصال به Google Earth Engine
+# Initialize Earth Engine
@st.cache_resource
-def initialize_gee():
+def initialize_earth_engine():
try:
- # مسیر فایل اعتبارسنجی سرویس اکانت
- service_account = 'esmaeilkiani13877@gmail.com'
- credentials_path = r"ee-esmaeilkiani13877-cfdea6eaf411.json"
+ 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_path)
+ credentials = ee.ServiceAccountCredentials(service_account, credentials_file)
ee.Initialize(credentials)
+
+ os.remove(credentials_file)
+
return True
except Exception as e:
- st.error(f"خطا در اتصال به Google Earth Engine: {e}")
+ st.error(f"خطا در اتصال به Earth Engine: {e}")
return False
-# بارگذاری و پردازش دادهها
+# Load data
@st.cache_data
def load_farm_data():
try:
- # بارگذاری دادههای مزارع
- farm_data = pd.read_csv('پایگاه داده (1).csv')
- farm_coordinates = pd.read_csv('farm_coordinates.csv')
+ 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']
+ # Simple formula for water requirement (mm/day)
+ water_requirement = (temperature - 20) * 0.5 + (100 - humidity) * 0.1
+ return max(0, water_requirement)
+ else:
+ return None
+
+# Create Earth Engine map with indices
+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=5)).strftime('%Y-%m-%d')
+ end_date = (date_obj + timedelta(days=5)).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', '#ff4500', '#ffd700', '#32cd32', '#006400']}
+ legend_title = 'شاخص پوشش گیاهی (NDVI)'
+ elif layer_type == "NDMI":
+ index = s2.normalizedDifference(['B8', 'B11']).rename('NDMI')
+ viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#8b0000', '#ff8c00', '#00ced1', '#00b7eb', '#00008b']}
+ legend_title = 'شاخص رطوبت (NDMI)'
+ elif layer_type == "EVI":
+ nir = s2.select('B8')
+ red = s2.select('B4')
+ blue = s2.select('B2')
+ index = nir.subtract(red).multiply(2.5).divide(nir.add(red.multiply(6)).subtract(blue.multiply(7.5)).add(1)).rename('EVI')
+ viz_params = {'min': 0, 'max': 1, 'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#4caf50']}
+ legend_title = 'شاخص پیشرفته گیاهی (EVI)'
+ elif layer_type == "NDWI":
+ index = s2.normalizedDifference(['B3', 'B8']).rename('NDWI')
+ viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#00008b', '#00b7eb', '#add8e6', '#fdae61', '#d73027']}
+ legend_title = 'شاخص آب (NDWI)'
+ elif layer_type == "SoilMoisture":
+ s1 = ee.ImageCollection('COPERNICUS/S1_GRD') \
+ .filterDate(start_date, end_date) \
+ .filterBounds(region) \
+ .sort('system:time_start') \
+ .first()
+ index = s1.select('VV').rename('SoilMoisture')
+ viz_params = {'min': -25, 'max': -5, 'palette': ['#00008b', '#add8e6', '#ffffff']}
+ legend_title = 'رطوبت خاک (Soil Moisture)'
+
+ 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 = '''
+
+
''' + legend_title + '''
+
+
+
+
+ '''
+ m.get_root().html.add_child(folium.Element(legend_html))
- return farm_data, farm_coordinates
+ return m
except Exception as e:
- st.error(f"خطا در بارگذاری دادهها: {e}")
- return None, None
+ st.error(f"خطا در ایجاد نقشه: {e}")
+ return None
-# توابع محاسبه شاخصهای گیاهی
-def calculate_ndvi(geometry, date):
- """محاسبه شاخص NDVI برای یک منطقه و تاریخ مشخص"""
- start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
- end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
-
- # استفاده از تصاویر Sentinel-2
- sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
- .filterDate(start_date, end_date) \
- .filterBounds(geometry) \
- .sort('CLOUDY_PIXEL_PERCENTAGE') \
- .first()
-
- # محاسبه NDVI
- ndvi = sentinel.normalizedDifference(['B8', 'B4']).rename('NDVI')
-
- # گرفتن آمار NDVI برای منطقه
- ndvi_stats = ndvi.reduceRegion(
- reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
- geometry=geometry,
- scale=10,
- maxPixels=1e9
- ).getInfo()
-
- return ndvi, ndvi_stats
+# 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)
+ }
+ }
-def calculate_ndwi(geometry, date):
- """محاسبه شاخص NDWI برای یک منطقه و تاریخ مشخص"""
- start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
- end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
-
- # استفاده از تصاویر Sentinel-2
- sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
- .filterDate(start_date, end_date) \
- .filterBounds(geometry) \
- .sort('CLOUDY_PIXEL_PERCENTAGE') \
- .first()
-
- # محاسبه NDWI (استفاده از باندهای NIR و SWIR)
- ndwi = sentinel.normalizedDifference(['B8', 'B11']).rename('NDWI')
-
- # گرفتن آمار NDWI برای منطقه
- ndwi_stats = ndwi.reduceRegion(
- reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
- geometry=geometry,
- scale=10,
- maxPixels=1e9
- ).getInfo()
-
- return ndwi, ndwi_stats
+# 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
+ }
-def calculate_evi(geometry, date):
- """محاسبه شاخص EVI برای یک منطقه و تاریخ مشخص"""
- start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
- end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
-
- # استفاده از تصاویر Sentinel-2
- sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
- .filterDate(start_date, end_date) \
- .filterBounds(geometry) \
- .sort('CLOUDY_PIXEL_PERCENTAGE') \
- .first()
-
- # محاسبه EVI
- # EVI = 2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))
- nir = sentinel.select('B8')
- red = sentinel.select('B4')
- blue = sentinel.select('B2')
-
- evi = nir.subtract(red).multiply(2.5).divide(
- nir.add(red.multiply(6)).subtract(blue.multiply(7.5)).add(1)
- ).rename('EVI')
-
- # گرفتن آمار EVI برای منطقه
- evi_stats = evi.reduceRegion(
- reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
- geometry=geometry,
- scale=10,
- maxPixels=1e9
- ).getInfo()
-
- return evi, evi_stats
+# Initialize Earth Engine
+ee_initialized = initialize_earth_engine()
-def calculate_ndmi(geometry, date):
- """محاسبه شاخص NDMI برای یک منطقه و تاریخ مشخص"""
- start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
- end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
-
- # استفاده از تصاویر Sentinel-2
- sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
- .filterDate(start_date, end_date) \
- .filterBounds(geometry) \
- .sort('CLOUDY_PIXEL_PERCENTAGE') \
- .first()
-
- # محاسبه NDMI (استفاده از باندهای NIR و SWIR)
- ndmi = sentinel.normalizedDifference(['B8', 'B11']).rename('NDMI')
-
- # گرفتن آمار NDMI برای منطقه
- ndmi_stats = ndmi.reduceRegion(
- reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
- geometry=geometry,
- scale=10,
- maxPixels=1e9
- ).getInfo()
-
- return ndmi, ndmi_stats
+# Load data
+farm_df = load_farm_data()
+coordinates_df = load_coordinates_data()
-def calculate_lai(geometry, date):
- """محاسبه شاخص LAI (سطح برگ) برای یک منطقه و تاریخ مشخص"""
- start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
- end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
-
- # استفاده از تصاویر Sentinel-2
- sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
- .filterDate(start_date, end_date) \
- .filterBounds(geometry) \
- .sort('CLOUDY_PIXEL_PERCENTAGE') \
- .first()
-
- # محاسبه LAI (تقریبی بر اساس NDVI)
- ndvi = sentinel.normalizedDifference(['B8', 'B4'])
- lai = ndvi.multiply(3.618).exp().subtract(0.118).rename('LAI')
-
- # گرفتن آمار LAI برای منطقه
- lai_stats = lai.reduceRegion(
- reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
- geometry=geometry,
- scale=10,
- maxPixels=1e9
- ).getInfo()
-
- return lai, lai_stats
+# 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')
-def calculate_chl(geometry, date):
- """محاسبه شاخص کلروفیل (CHL) برای یک منطقه و تاریخ مشخص"""
- start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
- end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
-
- # استفاده از تصاویر Sentinel-2
- sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
- .filterDate(start_date, end_date) \
- .filterBounds(geometry) \
- .sort('CLOUDY_PIXEL_PERCENTAGE') \
- .first()
-
- # محاسبه شاخص کلروفیل با استفاده از نسبت باندها
- # استفاده از باندهای قرمز لبه (Red Edge) و نزدیک مادون قرمز (NIR)
- re1 = sentinel.select('B5') # Red Edge 1
- re2 = sentinel.select('B6') # Red Edge 2
-
- chl = re2.divide(re1).rename('CHL')
-
- # گرفتن آمار CHL برای منطقه
- chl_stats = chl.reduceRegion(
- reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
- geometry=geometry,
- scale=10,
- maxPixels=1e9
- ).getInfo()
-
- return chl, chl_stats
+# 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'
+ ])
-# توابع ایجاد نمودارها و نقشهها
-def create_variety_pie_chart(farm_data):
- """ایجاد نمودار دایرهای برای نمایش توزیع واریتههای نیشکر"""
- variety_counts = farm_data['واریته'].value_counts()
-
- fig = px.pie(
- values=variety_counts.values,
- names=variety_counts.index,
- title="توزیع واریتههای نیشکر",
- color_discrete_sequence=px.colors.sequential.Greens_r
- )
+# Main header
+st.markdown('', unsafe_allow_html=True)
+st.markdown('
سامانه هوشمند پایش مزارع نیشکر دهخدا ', unsafe_allow_html=True)
+st.markdown('
پلتفرم جامع مدیریت، پایش و تحلیل دادههای مزارع نیشکر با استفاده از تصاویر ماهوارهای و هوش مصنوعی
', unsafe_allow_html=True)
+st.markdown('
', 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('', unsafe_allow_html=True)
+ st.markdown(f'
{len(farm_df)}
', unsafe_allow_html=True)
+ st.markdown('
تعداد مزارع
', unsafe_allow_html=True)
+ st.markdown('
', unsafe_allow_html=True)
+
+ with col2:
+ active_farms = int(len(farm_df) * 0.85)
+ st.markdown('', unsafe_allow_html=True)
+ st.markdown(f'
{active_farms}
', unsafe_allow_html=True)
+ st.markdown('
مزارع فعال
', unsafe_allow_html=True)
+ st.markdown('
', unsafe_allow_html=True)
+
+ with col3:
+ avg_height = 175
+ st.markdown('', unsafe_allow_html=True)
+ st.markdown(f'
{avg_height} cm
', unsafe_allow_html=True)
+ st.markdown('
میانگین ارتفاع
', unsafe_allow_html=True)
+ st.markdown('
', unsafe_allow_html=True)
+
+ with col4:
+ avg_moisture = 68
+ st.markdown('', unsafe_allow_html=True)
+ st.markdown(f'
{avg_moisture}%
', unsafe_allow_html=True)
+ st.markdown('
میانگین رطوبت
', unsafe_allow_html=True)
+ st.markdown('
', unsafe_allow_html=True)
+
+ tab1, tab2, tab3, tab4 = st.tabs(["نمای کلی", "نقشه مزارع", "نمودارها", "دادهها"])
- fig.update_layout(
- legend_title="واریته",
- font=dict(family="Vazirmatn"),
- hoverlabel=dict(font_family="Vazirmatn")
- )
+ 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(' ', unsafe_allow_html=True)
+ st_lottie(lottie_farm, height=300, key="farm_animation")
- return fig
+ 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"""
+
+
مزرعه {name}
+
واریته: {variety}
+
سن: {age}
+
مساحت: {area} هکتار
+
+ """
+ else:
+ popup_text = f"مزرعه {name}
"
+
+ 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('', unsafe_allow_html=True)
+ folium_static(m, width=1000, height=600)
+ st.markdown('
', 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("هیچ دادهای یافت نشد.")
-def create_age_pie_chart(farm_data):
- """ایجاد نمودار دایرهای برای نمایش توزیع سن مزارع نیشکر"""
- age_counts = farm_data['سن'].value_counts()
-
- fig = px.pie(
- values=age_counts.values,
- names=age_counts.index,
- title="توزیع سن مزارع نیشکر",
- color_discrete_sequence=px.colors.sequential.Greens_r
- )
+# Map Page
+elif selected == "نقشه مزارع":
+ st.markdown("## نقشه مزارع با شاخصهای ماهوارهای")
- fig.update_layout(
- legend_title="سن",
- font=dict(family="Vazirmatn"),
- hoverlabel=dict(font_family="Vazirmatn")
- )
+ col1, col2 = st.columns([1, 3])
- return fig
-
-def create_farm_map(farm_coordinates, selected_index=None, index_data=None):
- """ایجاد نقشه تعاملی مزارع با Folium"""
- # ایجاد نقشه با مرکزیت میانگین مختصات مزارع
- center_lat = farm_coordinates['lat'].mean()
- center_lon = farm_coordinates['lon'].mean()
-
- m = folium.Map(location=[center_lat, center_lon], zoom_start=10, control_scale=True)
-
- # اضافه کردن لایههای مختلف به نقشه
- folium.TileLayer('openstreetmap').add_to(m)
- folium.TileLayer('Stamen Terrain').add_to(m)
- folium.TileLayer('Stamen Toner').add_to(m)
- folium.TileLayer('Stamen Watercolor').add_to(m)
- folium.TileLayer('CartoDB positron').add_to(m)
-
- # اگر دادههای شاخص برای نمایش ارائه شده باشد
- if selected_index and index_data:
- # اضافه کردن لایه حرارتی بر اساس شاخص انتخاب شده
- heat_data = []
- for _, row in farm_coordinates.iterrows():
- farm_id = row['farm_id']
- if farm_id in index_data:
- heat_data.append([row['lat'], row['lon'], index_data[farm_id]])
-
- HeatMap(heat_data, radius=15, max_zoom=13, blur=10).add_to(m)
-
- # اضافه کردن نشانگرها برای هر مزرعه
- for _, row in farm_coordinates.iterrows():
- popup_text = f"""
-
- شناسه مزرعه: {row['farm_id']}
- واریته: {row['variety']}
- سن: {row['age']} سال
- مساحت: {row['area']} هکتار
-
- """
+ with col1:
+ st.markdown('', unsafe_allow_html=True)
+ st.markdown("### تنظیمات نقشه")
- folium.Marker(
- location=[row['lat'], row['lon']],
- popup=folium.Popup(popup_text, max_width=300),
- tooltip=f"مزرعه {row['farm_id']}",
- icon=folium.Icon(color='green', icon='leaf', prefix='fa')
- ).add_to(m)
-
- # اضافه کردن کنترل لایهها
- folium.LayerControl().add_to(m)
+ 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('
', 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('', unsafe_allow_html=True)
- return m
+ with col2:
+ map_tab, stats_tab = st.tabs(["نقشه", "آمار و تحلیل"])
+
+ with map_tab:
+ st.markdown('', 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('
', 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('', unsafe_allow_html=True)
+ st.markdown(f'
{stats["mean"]}
', unsafe_allow_html=True)
+ st.markdown(f'
میانگین {selected_layer}
', unsafe_allow_html=True)
+ st.markdown('
', unsafe_allow_html=True)
+
+ with col2:
+ st.markdown('', unsafe_allow_html=True)
+ st.markdown(f'
{stats["max"]}
', unsafe_allow_html=True)
+ st.markdown(f'
حداکثر {selected_layer}
', unsafe_allow_html=True)
+ st.markdown('
', unsafe_allow_html=True)
+
+ with col3:
+ st.markdown('', unsafe_allow_html=True)
+ st.markdown(f'
{stats["min"]}
', unsafe_allow_html=True)
+ st.markdown(f'
حداقل {selected_layer}
', unsafe_allow_html=True)
+ st.markdown('
', unsafe_allow_html=True)
+
+ with col4:
+ st.markdown('', unsafe_allow_html=True)
+ st.markdown(f'
{stats["std_dev"]}
', unsafe_allow_html=True)
+ st.markdown(f'
انحراف معیار
', unsafe_allow_html=True)
+ st.markdown('
', 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("لطفاً ابتدا یک نقشه تولید کنید.")
-def create_growth_chart(farm_data, farm_id, index_type='NDVI'):
- """ایجاد نمودار خطی برای نمایش روند رشد مزرعه بر اساس شاخص انتخابی"""
- # فیلتر دادهها برای مزرعه مورد نظر
- farm_history = farm_data[farm_data['farm_id'] == farm_id].sort_values('date')
-
- # ایجاد نمودار خطی
- fig = px.line(
- farm_history,
- x='date',
- y=index_type,
- title=f"روند شاخص {index_type} برای مزرعه {farm_id}",
- markers=True
- )
+# Data Entry Page
+elif selected == "ورود اطلاعات":
+ st.markdown("## ورود اطلاعات روزانه مزارع")
- fig.update_layout(
- xaxis_title="تاریخ",
- yaxis_title=f"شاخص {index_type}",
- font=dict(family="Vazirmatn"),
- hoverlabel=dict(font_family="Vazirmatn")
- )
+ tab1, tab2 = st.tabs(["ورود دستی", "آپلود فایل"])
- return fig
+ 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=["شنبه", "یکشنبه", "دوشنبه", "سهشنبه", "چهارشنبه", "پنجشنبه"]
+ )
+
+ 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("""
+
+
+
+
+
+
+
فایل خود را اینجا رها کنید یا روی دکمه بالا کلیک کنید
+
+ """, unsafe_allow_html=True)
-def create_index_distribution(farm_data, index_type='NDVI'):
- """ایجاد نمودار توزیع شاخص انتخابی برای تمام مزارع"""
- fig = px.histogram(
- farm_data,
- x=index_type,
- title=f"توزیع شاخص {index_type} در مزارع",
- color_discrete_sequence=['green']
- )
+# Data Analysis Page
+elif selected == "تحلیل دادهها":
+ st.markdown("## تحلیل هوشمند دادهها")
- fig.update_layout(
- xaxis_title=f"شاخص {index_type}",
- yaxis_title="تعداد مزارع",
- font=dict(family="Vazirmatn"),
- hoverlabel=dict(font_family="Vazirmatn")
- )
+ col1, col2 = st.columns([1, 2])
- return fig
-
-def create_correlation_heatmap(farm_data):
- """ایجاد نقشه حرارتی همبستگی بین شاخصهای مختلف"""
- # انتخاب ستونهای شاخص
- index_columns = ['NDVI', 'NDWI', 'EVI', 'NDMI', 'LAI', 'CHL']
- correlation_data = farm_data[index_columns].corr()
-
- fig = px.imshow(
- correlation_data,
- text_auto=True,
- color_continuous_scale='Viridis',
- title="همبستگی بین شاخصهای گیاهی"
- )
+ with col1:
+ st_lottie(lottie_analysis, height=200, key="analysis_animation")
- fig.update_layout(
- font=dict(family="Vazirmatn"),
- hoverlabel=dict(font_family="Vazirmatn")
- )
+ with col2:
+ st.markdown("""
+
+
تحلیل پیشرفته دادههای مزارع
+
در این بخش میتوانید تحلیلهای پیشرفته روی دادههای مزارع انجام دهید و روندها و الگوهای مختلف را بررسی کنید.
+
+ """, unsafe_allow_html=True)
- return fig
-
-# توابع مربوط به یادگیری ماشین و تحلیل داده
-def train_growth_prediction_model(farm_data, target_index='NDVI'):
- """آموزش مدل پیشبینی رشد با استفاده از Random Forest"""
- # آمادهسازی دادهها
- X = farm_data[['سن', 'رطوبت', 'دما', 'بارش']] # ویژگیهای ورودی
- y = farm_data[target_index] # هدف پیشبینی
-
- # آموزش مدل
- model = RandomForestRegressor(n_estimators=100, random_state=42)
- model.fit(X, y)
-
- return model
-
-def predict_growth(model, new_data):
- """پیشبینی رشد با استفاده از مدل آموزش دیده"""
- predictions = model.predict(new_data)
- return predictions
-
-def detect_stress(farm_data, ndvi_threshold=0.4, ndwi_threshold=0.2):
- """شناسایی مزارع با تنش آبی یا بیماری بر اساس آستانههای شاخص"""
- # شناسایی مزارع با NDVI پایین (تنش یا بیماری)
- low_ndvi_farms = farm_data[farm_data['NDVI'] < ndvi_threshold]
+ tab1, tab2, tab3, tab4 = st.tabs(["تحلیل رشد", "مقایسه واریتهها", "تحلیل رطوبت", "پیشبینی"])
- # شناسایی مزارع با NDWI پایین (تنش آبی)
- low_water_farms = farm_data[farm_data['NDWI'] < ndwi_threshold]
+ 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("### تحلیل رطوبت مزارع")
+
+ farms = farm_df['مزرعه'].unique()[:10]
+ dates = pd.date_range(end=datetime.now(), periods=30, freq='D')
+
+ moisture_data = []
+ for farm in farms:
+ base_moisture = np.random.uniform(50, 80)
+ for date in dates:
+ moisture = base_moisture + np.random.normal(0, 5)
+ moisture = max(0, min(100, moisture))
+ moisture_data.append({
+ 'Farm': farm,
+ 'Date': date,
+ 'Moisture': moisture
+ })
+
+ moisture_df = pd.DataFrame(moisture_data)
+
+ fig = px.line(
+ moisture_df,
+ x='Date',
+ y='Moisture',
+ color='Farm',
+ title='روند رطوبت مزارع در 30 روز گذشته',
+ labels={'Date': 'تاریخ', 'Moisture': 'رطوبت (%)', 'Farm': 'مزرعه'}
+ )
+ fig.update_layout(
+ font=dict(family="Vazirmatn"),
+ template="plotly_white",
+ hovermode="x unified"
+ )
+ st.plotly_chart(fig, use_container_width=True)
+
+ st.markdown("### همبستگی رطوبت و ارتفاع")
+
+ correlation_data = []
+ for farm in farms:
+ for _ in range(20):
+ moisture = np.random.uniform(40, 90)
+ height = 100 + moisture * 1.5 + np.random.normal(0, 20)
+ correlation_data.append({
+ 'Farm': farm,
+ 'Moisture': moisture,
+ 'Height': height
+ })
+
+ correlation_df = pd.DataFrame(correlation_data)
+
+ fig = px.scatter(
+ correlation_df,
+ x='Moisture',
+ y='Height',
+ color='Farm',
+ title='همبستگی بین رطوبت و ارتفاع',
+ labels={'Moisture': 'رطوبت (%)', 'Height': 'ارتفاع (سانتیمتر)', 'Farm': 'مزرعه'},
+ trendline='ols'
+ )
+ fig.update_layout(
+ font=dict(family="Vazirmatn"),
+ template="plotly_white"
+ )
+ st.plotly_chart(fig, use_container_width=True)
+
+ correlation = correlation_df['Moisture'].corr(correlation_df['Height'])
+ st.info(f"ضریب همبستگی بین رطوبت و ارتفاع: {correlation:.2f}")
- return low_ndvi_farms, low_water_farms
-
-# توابع مربوط به گزارشگیری
-def generate_pdf_report(farm_data, report_type, start_date, end_date):
- """تولید گزارش PDF بر اساس نوع گزارش و بازه زمانی"""
- buffer = io.BytesIO()
- doc = SimpleDocTemplate(buffer, pagesize=letter)
- elements = []
-
- # افزودن عنوان گزارش
- title = f"گزارش {report_type} مزارع نیشکر از {start_date} تا {end_date}"
- elements.append(Paragraph(title, getSampleStyleSheet()['Heading1']))
-
- # افزودن جدول دادهها
- table_data = [list(farm_data.columns)]
- for _, row in farm_data.iterrows():
- table_data.append(list(row.values))
-
- t = Table(table_data)
- t.setStyle(TableStyle([
- ('BACKGROUND', (0, 0), (-1, 0), colors.green),
- ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
- ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
- ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
- ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
- ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
- ('GRID', (0, 0), (-1, -1), 1, colors.black)
- ]))
-
- elements.append(t)
-
- # ساخت PDF
- doc.build(elements)
- buffer.seek(0)
-
- return buffer
-
-def download_button(object_to_download, download_filename, button_text):
- """ایجاد دکمه دانلود برای فایل"""
- b64 = base64.b64encode(object_to_download.getvalue()).decode()
- button_uuid = str(uuid.uuid4()).replace('-', '')
- button_id = re.sub('\d+', '', button_uuid)
-
- custom_css = f"""
-
- """
-
- dl_link = custom_css + f'{button_text} '
-
- return st.markdown(dl_link, unsafe_allow_html=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)
-# بخش اصلی برنامه
-def main():
- # شروع مقدارسازی Google Earth Engine
- gee_initialized = initialize_gee()
-
- # بارگذاری دادههای مزارع
- farm_data, farm_coordinates = load_farm_data()
-
- # منوی افقی
- st.markdown(
- """
-