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( - """ -
- داشبورد - نقشه مزارع - ورود اطلاعات - تحلیل داده‌ها - گزارش‌گیری - تنظیمات +# 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(""" +
+

گزارش‌گیری پیشرفته

+

در این بخش می‌توانید گزارش‌های مختلف از وضعیت مزارع تهیه کنید و آن‌ها را به صورت PDF یا Excel دانلود کنید.

- """, - unsafe_allow_html=True + """, 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=["گزارش کلی", "گزارش رشد", "گزارش رطوبت", "گزارش مقایسه‌ای واریته‌ها"] ) - # باکس سمت راست برای انتخاب گزینه‌ها - with st.sidebar: - st.markdown("

سامانه هوشمند پایش مزارع نیشکر

", unsafe_allow_html=True) - st_lottie(lottie_farm, height=200) + 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("

تنظیمات

", unsafe_allow_html=True) + 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") - # انتخاب تاریخ - selected_date = st.date_input( - "تاریخ مورد نظر", - datetime.now().date(), - format="YYYY/MM/DD" + 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 ) - # انتخاب مزرعه - if farm_coordinates is not None: - farm_options = farm_coordinates['farm_id'].unique() - selected_farm = st.selectbox("انتخاب مزرعه", farm_options, 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 + ) - # انتخاب شاخص - index_options = ['NDVI', 'NDWI', 'EVI', 'NDMI', 'LAI', 'CHL'] - selected_index = st.selectbox("انتخاب شاخص", index_options, index=0) + st.markdown("#### تنظیمات مدل هوش مصنوعی") + ai_model = st.selectbox( + "مدل هوش مصنوعی", + options=["GPT-3", "GPT-4", "BERT"], + index=1 + ) + model_update_frequency = st.selectbox( + "تناوب به‌روزرسانی مدل", + options=["روزانه", "هفتگی", "ماهانه"], + index=1 + ) - # دکمه اعمال تغییرات - apply_btn = st.button("اعمال تغییرات", type="primary") + if st.button("ذخیره تنظیمات", type="primary"): + st.success("تنظیمات با موفقیت ذخیره شدند.") - # نمایش داشبورد (صفحه اصلی) - tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs(["داشبورد", "نقشه مزارع", "ورود اطلاعات", "تحلیل داده‌ها", "گزارش‌گیری", "تنظیمات"]) + 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 tab1: - st.markdown("

داشبورد مدیریت مزارع نیشکر

", unsafe_allow_html=True) + with tab4: + st.markdown("### پشتیبان‌گیری و بازیابی") - # کارت‌های اطلاعات کلیدی - col1, col2, col3, col4 = st.columns(4) + 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("نسخه پشتیبان با موفقیت ایجاد شد.") - with col1: - st.markdown( - """ -
-
🌱
-
-
تعداد کل مزارع
-
125
-
-
- """, - unsafe_allow_html=True + 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) - with col2: - st.markdown( - """ -
-
🚜
-
""" - ) \ No newline at end of file + if st.button("ذخیره تنظیمات پشتیبان‌گیری"): + st.success("تنظیمات پشتیبان‌گیری با موفقیت ذخیره شدند.") + +# Add a footer +st.markdown(""" + +""", unsafe_allow_html=True)import streamlit as st