diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -1,1907 +1,568 @@ import streamlit as st - -# تعریف نام ستون‌های فایل -column_names = { - 0: "سال", - 1: "هفته", - 2: "مزرعه", - 3: "کانال", - 4: "اداره", - 5: "مساحت", - 6: "مساحت زیر مجموعه", - 7: "رقم", - 8: "سن", - 9: "ایستگاه 1", - 10: "ایستگاه 2", - 11: "ایستگاه 3", - 12: "ایستگاه 4", - 13: "ایستگاه 5", - 14: "ارتفاع هفته جاری مزرعه", - 15: "ارتفاع هفته گذشته مزرعه", - 16: "رشد هفته جاری", - 17: "رشد هفته گذشته", - 18: "نیتروژن فعلی", - 19: "نیتروژن استاندارد فعلی", - 20: "نیتروژن قبلی", - 21: "نیتروژن استاندارد قبلی", - 22: "رطوبت غلاف فعلی", - 23: "رطوبت استاندارد فعلی", - 24: "رطوبت غلاف قبلی", - 25: "رطوبت استاندارد قبلی", - 26: "چاهک 1", - 27: "تاریخ قرائت", - 28: "چاهک 2", - 29: "تاریخ قرائت.1" -} import pandas as pd import numpy as np -import folium -from streamlit_folium import folium_static +import geemap.foliumap as geemap import ee import os -import json -import time -from datetime import datetime, timedelta +import datetime import plotly.express as px import plotly.graph_objects as go -from PIL import Image +from streamlit_folium import folium_static +import folium +from folium.plugins import Draw, Fullscreen, MeasureControl +import json import base64 -from io import BytesIO -import matplotlib.pyplot as plt -import seaborn as sns -import altair as alt -from streamlit_option_menu import option_menu -from streamlit_lottie import st_lottie -import requests -import hydralit_components as hc -from streamlit_extras.colored_header import colored_header -from streamlit_extras.metric_cards import style_metric_cards -from streamlit_extras.chart_container import chart_container -from streamlit_extras.add_vertical_space import add_vertical_space -from streamlit_card import card -import pydeck as pdk -import math -from sklearn.linear_model import LinearRegression +from PIL import Image +import io -# تنظیمات صفحه با تم سفارشی +# تنظیم عنوان صفحه st.set_page_config( - page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا", + page_title="داشبورد مانیتورینگ مزارع نیشکر دهخدا", page_icon="🌱", layout="wide", - initial_sidebar_state="expanded" + initial_sidebar_state="expanded", ) -# CSS سفارشی با طراحی مدرن و جذاب +# استایل‌های CSS st.markdown(""" """, unsafe_allow_html=True) -# بارگذاری داده‌های واقعی مزارع از CSV -@st.cache_data -def load_farm_data(): - try: - df = pd.read_csv("کراپ لاگ کلی (1).csv") - st.write("ستون‌های موجود در فایل:", df.columns.tolist()) - df.rename(columns={ - 'سال': 'Year', - 'هفته': 'Week', - 'مزرعه': 'Farm_ID', - 'کانال': 'Channel', - 'اداره': 'Administration', - 'مساحت': 'Area', - 'مساحت زیر مجموعه': 'SubArea', - 'رقم': 'Variety', - 'سن': 'Age', - 'ایستگاه 1': 'Station1', - 'ایستگاه 2': 'Station2', - 'ایستگاه 3': 'Station3', - 'ایستگاه 4': 'Station4', - 'ایستگاه 5': 'Station5', - 'ارتفاع هفته جاری مزرعه': 'CurrentHeight', - 'ارتفاع هفته گذشته مزرعه': 'PreviousHeight', - 'رشد هفته جاری': 'CurrentGrowth', - 'رشد هفته گذشته': 'PreviousGrowth', - 'نیتروژن فعلی': 'CurrentNitrogen', - 'نیتروژن استاندارد فعلی': 'StandardNitrogen', - 'نیتروژن قبلی': 'PreviousNitrogen', - 'نیتروژن استاندارد قبلی': 'PreviousStandardNitrogen', - 'رطوبت غلاف فعلی': 'CurrentMoisture', - 'رطوبت استاندارد فعلی': 'StandardMoisture', - 'رطوبت غلاف قبلی': 'PreviousMoisture', - 'رطوبت استاندارد قبلی': 'PreviousStandardMoisture', - 'چاهک 1': 'Well1', - 'تاریخ قرائت': 'Well1Date', - 'چاهک 2': 'Well2', - 'تاریخ قرائت.1': 'Well2Date' - }, inplace=True, errors='ignore') - - # Convert numeric columns to appropriate types - numeric_columns = ['CurrentHeight', 'PreviousHeight', 'CurrentGrowth', 'PreviousGrowth', - 'CurrentNitrogen', 'StandardNitrogen', 'PreviousNitrogen', 'PreviousStandardNitrogen', - 'CurrentMoisture', 'StandardMoisture', 'PreviousMoisture', 'PreviousStandardMoisture', - 'Well1', 'Well2', 'Station1', 'Station2', 'Station3', 'Station4', 'Station5'] - - for col in numeric_columns: - if col in df.columns: - df[col] = pd.to_numeric(df[col], errors='coerce') - - return df - except Exception as e: - st.error(f"خطا در بارگذاری داده‌های مزارع: {e}") - return pd.DataFrame() - -@st.cache_data -def load_coordinates_data(): - try: - coords_df = pd.read_csv("farm_coordinates.csv") - coords_df.rename(columns={ - 'مزرعه': 'Farm_ID', - 'عرض جغرافیایی': 'Latitude', - 'طول جغرافیایی': 'Longitude' - }, inplace=True, errors='ignore') - return coords_df - except Exception as e: - st.error(f"خطا در بارگذاری داده‌های مختصات: {e}") - return pd.DataFrame() - -@st.cache_data -def load_day_data(): - try: - day_df = pd.read_csv("پایگاه داده (1).csv") - day_df.rename(columns={ - 'سال': 'Year', - 'هفته': 'Week', - 'مزرعه': 'Farm_ID', - 'روز': 'Day' - }, inplace=True, errors='ignore') - return day_df - except Exception as e: - st.error(f"خطا در بارگذاری داده‌های روزهای هفته: {e}") - return pd.DataFrame() - -# بارگذاری انیمیشن JSON -@st.cache_data -def load_lottie_url(url: str): - r = requests.get(url) - if r.status_code != 200: - return None - return r.json() - -# مقداردهی اولیه Earth Engine +# تابع برای احراز هویت GEE @st.cache_resource -def initialize_earth_engine(): +def initialize_gee(): try: service_account = 'dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com' - credentials_dict = { - "type": "service_account", - "project_id": "ee-esmaeilkiani13877", - "private_key_id": "cfdea6eaf4115cb6462626743e4b15df85fd0c7f", - "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCjeOvgKi+gWK6k\n2/0RXOA3LAo51DVxA1ja9v0qFOn4FNOStxkwlKvcK8yDQNb53FPORHFIUHvev3y7\niHr/UEUqnn5Rzjbf0k3qWB/fS377/UP4VznMsFpKiHNxCBtaNS8KLk6Rat6Y7Xfm\nJfpSU7ZjYZmVc81M/7iFofGUSJoHYpxhyt3rjp53huxJNNW5e12TFnLkyg1Ja/9X\nGMTt+vjVcO4XhQCIlaGVdSKS2sHlHgzpzE6KtuUKjDMEBqPkWF4xc16YavYltwPd\nqULCu2/t6dczhYL4NEFj8wL+KJqOojfsyoWmzqPFx1Bbxk4BVPk/lslq9+m9p5kq\nSCG0/9W9AgMBAAECggEAEGchw+x3uu8rFv+79PIMzXxtyj+w3RYo5E/EN2TB1VLB\nqAcXT/ibBgyfCMyIxamF/zx+4XKx+zfbnDWlodi8F/qvUiYO+4ZuqwUMras1orNX\nDqQx+If5h2EJtF3L4NFVVwAuggjnLREm5sEIzRn5Qx+X+ZcVEpTWPxJw2yAt1G+3\nk311KqD+zR7jQfchXU4xQQ1ZoHkdAJ/DPGun6x+HUOq7Gus73A6IzLp12ZoiHN3n\nkY+lG8cMs039QAe/OhZFEo5I9cNSaI688HmsLRivZ26WoPEnwcN0MHQGtXqGmMUI\nCcpgJqllqdWMuBlYcpSadn7rZzPujSlzIxkvieLeAQKBgQDNTYUWZdGbA2sHcBpJ\nrqKwDYF/AwZtjx+hXHVBRbR6DJ1bO2P9n51ioTMP/H9K61OBAMZ7w71xJ2I+9Snv\ncYumPWoiUwiOhTh3O7nYz6mR7sK0HuUCZfYdaxJVnLgNCgj+w9AxYnkzOyL9/QvJ\nknrlMKf4H59NbapBqy5spilq1QKBgQDL1wkGHhoTuLb5Xp3X3CX4S7WMke4T01bO\nPpMmlewVgH5lK5wTcZjB8QRO2QFQtUZTP/Ghv6ZH4h/3P9/ZIF3hV5CSsUkr/eFf\nMY+fQL1K/puwfZbSDcH1GtDToOyoLFIvPXBJo0Llg/oF2TK1zGW3cPszeDf/Tm6x\nUwUMw2BjSQKBgEJzAMyLEBi4NoAlzJxkpcuN04gkloQHexljL6B8yzlls9i/lFGW\nw/4UZs6ZzymUmWZ7tcKBTGO/d5EhEP2rJqQb5KpPbcmTXP9amYCPVjchrGtYRI9O\nKSbEbR7ApuGxic/L2Sri0I/AaEcFDDel7ZkY8oTg11LcV+sBWPlZnrYxAoGBALXj\n/DlpQvu2KA/9TfwAhiE57Zax4S/vtdX0IHqd7TyCnEbK00rGYvksiBuTqIjMOSSw\nOn2K9mXOcZe/d4/YQe2CpY9Ag3qt4R2ArBf/POpep66lYp+thxWgCBfP0V1/rxZY\nTIppFJiZW9E8LvPqoBlAx+b1r4IyCrRQ0IDDFo+BAoGBAMCff4XKXHlV2SDOL5uh\nV/f9ApEdF4leuo+hoMryKuSQ9Y/H0A/Lzw6KP5FLvVtqc0Kw2D1oLy8O72a1xwfY\n8dpZMNzKAWWS7viN0oC+Ebj2Foc2Mn/J6jdhtP/YRLTqvoTWCa2rVcn4R1BurMIf\nLa4DJE9BagGdVNTDtynBhKhZ\n-----END PRIVATE KEY-----\n", - "client_email": "dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com", - "client_id": "113062529451626176784", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dehkhodamap-e9f0da4ce9f6514021%40ee-esmaeilkiani13877.iam.gserviceaccount.com", - "universe_domain": "googleapis.com" - } credentials_file = 'ee-esmaeilkiani13877-cfdea6eaf411.json' - with open(credentials_file, 'w') as f: - json.dump(credentials_dict, f) credentials = ee.ServiceAccountCredentials(service_account, credentials_file) ee.Initialize(credentials) - os.remove(credentials_file) return True except Exception as e: - st.error(f"خطا در اتصال به Earth Engine: {e}") + st.error(f"خطا در اتصال به Google Earth Engine: {e}") return False -# ایج��د نقشه Earth Engine با شاخص‌ها -def create_ee_map(farm_id, date_str, layer_type="NDVI"): +# خواندن داده‌های CSV +@st.cache_data +def load_data(): try: - farm_row = coordinates_df[coordinates_df['Farm_ID'] == farm_id].iloc[0] - lat, lon = farm_row['Latitude'], farm_row['Longitude'] - 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)' - 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', prefix='fa') - ).add_to(m) - folium.Circle( - [lat, lon], - radius=1500, - color='green', - fill=True, - fill_color='green', - fill_opacity=0.1, - weight=2 - ).add_to(m) - folium.LayerControl().add_to(m) - legend_html = ''' -
-
''' + legend_title + '''
-
-
- کم -
-
-
- متوسط -
-
-
- زیاد -
-
- ''' - m.get_root().html.add_child(folium.Element(legend_html)) - return m + df = pd.read_csv('output (1).csv') + return df except Exception as e: - st.error(f"خطا در ایجاد نقشه: {e}") + st.error(f"خطا در خواندن فایل CSV: {e}") return None -# محاسبه آمار واقعی مزارع -def calculate_farm_stats(farm_id, layer_type="NDVI"): - farm_data = farm_df[farm_df['Farm_ID'] == farm_id] - if layer_type == "NDVI": - stats = { - 'mean': farm_data['CurrentHeight'].mean() if not farm_data.empty else 0, - 'min': farm_data['CurrentHeight'].min() if not farm_data.empty else 0, - 'max': farm_data['CurrentHeight'].max() if not farm_data.empty else 0, - 'std_dev': farm_data['CurrentHeight'].std() if not farm_data.empty else 0, - 'histogram_data': farm_data['CurrentHeight'].values if not farm_data.empty else np.array([]) - } - elif layer_type == "NDMI": - stats = { - 'mean': farm_data['CurrentMoisture'].mean() if not farm_data.empty else 0, - 'min': farm_data['CurrentMoisture'].min() if not farm_data.empty else 0, - 'max': farm_data['CurrentMoisture'].max() if not farm_data.empty else 0, - 'std_dev': farm_data['CurrentMoisture'].std() if not farm_data.empty else 0, - 'histogram_data': farm_data['CurrentMoisture'].values if not farm_data.empty else np.array([]) +# تابع برای محاسبه شاخص‌های مختلف +def calculate_indices(image): + # NDVI + ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI') + + # EVI + evi = image.expression( + '2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))', + { + 'NIR': image.select('B8'), + 'RED': image.select('B4'), + 'BLUE': image.select('B2') } - return stats - -# تولید داده‌های رشد واقعی -def generate_real_growth_data(selected_variety="all", selected_age="all"): - filtered_farms = farm_df - if selected_variety != "all": - filtered_farms = filtered_farms[filtered_farms['Variety'] == selected_variety] - if selected_age != "all": - filtered_farms = filtered_farms[filtered_farms['Age'] == selected_age] + ).rename('EVI') - farm_growth_data = [] - weeks = filtered_farms['Week'].unique() - for farm_id in filtered_farms['Farm_ID'].unique(): - farm_data = filtered_farms[filtered_farms['Farm_ID'] == farm_id] - growth_data = { - 'farm_id': farm_id, - 'variety': farm_data['Variety'].iloc[0] if not farm_data.empty else 'Unknown', - 'age': farm_data['Age'].iloc[0] if not farm_data.empty else 'Unknown', - 'weeks': weeks, - 'heights': [farm_data[farm_data['Week'] == week]['CurrentHeight'].mean() if not farm_data[farm_data['Week'] == week].empty else 0 for week in weeks] + # NDMI (Normalized Difference Moisture Index) + ndmi = image.normalizedDifference(['B8', 'B11']).rename('NDMI') + + # LAI (Leaf Area Index) - simplified model + lai = image.expression( + '3.618 * EVI - 0.118', + { + 'EVI': evi } - farm_growth_data.append(growth_data) + ).rename('LAI') - if farm_growth_data: - avg_heights = [] - for week in weeks: - week_heights = [farm['heights'][list(weeks).index(week)] for farm in farm_growth_data if farm['heights'][list(weeks).index(week)] > 0] - avg_heights.append(np.mean(week_heights) if week_heights else 0) - - avg_growth_data = { - 'farm_id': 'میانگین', - 'variety': 'همه', - 'age': 'همه', - 'weeks': weeks, - 'heights': avg_heights + # Biomass - simplified model based on LAI + biomass = image.expression( + '0.8 * LAI + 0.2', + { + 'LAI': lai } - return {'individual': farm_growth_data, 'average': avg_growth_data} - return { - 'individual': [], - 'average': {'farm_id': 'میانگین', 'variety': 'همه', 'age': 'همه', 'weeks': weeks, 'heights': [0] * len(weeks)} - } + ).rename('Biomass') + + # MSI (Moisture Stress Index) + msi = image.expression( + 'SWIR1 / NIR', + { + 'SWIR1': image.select('B11'), + 'NIR': image.select('B8') + } + ).rename('MSI') + + # Chlorophyll Index + chlorophyll = image.expression( + 'NIR / RED - 1', + { + 'NIR': image.select('B8'), + 'RED': image.select('B4') + } + ).rename('Chlorophyll') + + # Add all indices to the image + return image.addBands([ndvi, evi, ndmi, lai, biomass, msi, chlorophyll]) -# مقداردهی اولیه Earth Engine و بارگذاری داده‌ها -ee_initialized = initialize_earth_engine() -farm_df = load_farm_data() -coordinates_df = load_coordinates_data() -day_df = load_day_data() +# تابع برای دریافت تصاویر Sentinel-2 +def get_sentinel_imagery(start_date, end_date, aoi): + # فیلتر کردن مجموعه داده Sentinel-2 + s2 = ee.ImageCollection('COPERNICUS/S2_SR') \ + .filterDate(start_date, end_date) \ + .filterBounds(aoi) \ + .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20)) + + if s2.size().getInfo() == 0: + return None + + # محاسبه میانگین تصاویر + s2_median = s2.median() + + # محاسبه شاخص‌ها + s2_indices = calculate_indices(s2_median) + + return s2_indices -# بارگذاری انیمیشن‌ها -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 create_map(center_lat, center_lon, zoom=13): + m = geemap.Map() + m.set_center(center_lon, center_lat, zoom) + m.add_basemap('HYBRID') + return m -# ایجاد حالت جلسه برای ذخیره داده‌ها -if 'heights_df' not in st.session_state: - st.session_state.heights_df = farm_df.copy() +# تابع برای اضافه کردن لایه شاخص به نقشه +def add_index_layer(m, image, index_name, vis_params, layer_name): + if image is not None: + m.add_layer(image.select(index_name), vis_params, layer_name) + return m -# سرصفحه اصلی با انیمیشن -st.markdown('
', unsafe_allow_html=True) -st.markdown('

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

', unsafe_allow_html=True) -st.markdown('

پلتفرم جامع مدیریت، پایش و تحلیل داده‌های مزارع نیشکر با هوش مصنوعی پیشرفته

', unsafe_allow_html=True) -st.markdown('
', unsafe_allow_html=True) +# تابع برای ایجاد نمودار زمانی +def create_time_series(aoi, index_name, start_date, end_date, interval='day'): + # تعریف بازه‌های زمانی + if interval == 'day': + step = 1 + unit = 'day' + elif interval == 'week': + step = 7 + unit = 'day' + elif interval == 'month': + step = 1 + unit = 'month' + + # ایجاد لیست تاریخ‌ها + dates = [] + values = [] + + current_date = start_date + while current_date <= end_date: + next_date = current_date + datetime.timedelta(days=step) if unit == 'day' else \ + datetime.date(current_date.year + (current_date.month + step - 1) // 12, + (current_date.month + step - 1) % 12 + 1, + 1) + + # دریافت تصویر برای این بازه زمانی + image = get_sentinel_imagery(current_date.strftime('%Y-%m-%d'), + next_date.strftime('%Y-%m-%d'), + aoi) + + if image is not None: + # محاسبه میانگین شاخص در منطقه مورد نظر + mean_value = image.select(index_name).reduceRegion( + reducer=ee.Reducer.mean(), + geometry=aoi, + scale=10 + ).get(index_name).getInfo() + + if mean_value is not None: + dates.append(current_date.strftime('%Y-%m-%d')) + values.append(mean_value) + + current_date = next_date + + # ایجاد دیتافریم برای نمودار + if len(dates) > 0: + df = pd.DataFrame({ + 'Date': dates, + index_name: values + }) + return df + else: + return None -# ایجاد منوی ناوبری جذاب -selected = option_menu( - menu_title=None, - options=["داشبورد", "نقشه مزارع", "ورود اطلاعات", "تحلیل داده‌ها", "گزارش‌گیری", "تنظیمات"], - icons=["speedometer2", "map", "pencil-square", "bar-chart", "file-text", "gear"], - menu_icon="leaf", - default_index=0, - orientation="horizontal", - styles={ - "container": {"padding": "0!important", "background-color": "transparent", "margin-bottom": "25px"}, - "icon": {"color": "#2ecc71", "font-size": "22px"}, - "nav-link": {"font-size": "18px", "text-align": "center", "margin": "5px", "--hover-color": "#e9f7ef", "border-radius": "12px"}, - "nav-link-selected": {"background": "linear-gradient(135deg, #2ecc71 0%, #27ae60 100%)", "color": "white", "font-weight": "700", "box-shadow": "0 10px 30px rgba(46, 204, 113, 0.4)"}, - } -) +# تابع برای دانلود تصویر نقشه +def get_map_download_link(m, filename="map.html"): + """تولید لینک دانلود برای نقشه""" + m.to_html(filename) + with open(filename, 'rb') as f: + html_data = f.read() + + b64 = base64.b64encode(html_data).decode() + href = f'دانلود نقشه' + return href -# داشبورد -if selected == "داشبورد": - st.markdown('
', unsafe_allow_html=True) - st.markdown("### نمای کلی مزارع", unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) +# تابع برای رتبه‌بندی مزارع بر اساس شاخص‌ها +def rank_farms(df, index_values): + """رتبه‌بندی مزارع بر اساس مقادیر شاخص‌ها""" + if df is None or index_values is None or len(index_values) == 0: + return None - # معیارهای داشبورد با انیمیشن - col1, col2, col3, col4 = st.columns(4) + # ترکیب داده‌های مزارع با مقادیر شاخص‌ها + farm_ranks = df.copy() - with col1: - st.markdown('
', unsafe_allow_html=True) - st.markdown(f'
{len(farm_df["Farm_ID"].unique())}
', unsafe_allow_html=True) - st.markdown('
تعداد مزارع
', unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) + # اضافه کردن مقادیر شاخص‌ها (در اینجا فرض می‌کنیم که index_values یک دیکشنری است) + for farm_id, values in index_values.items(): + for index_name, value in values.items(): + farm_ranks.loc[farm_ranks['مزرعه'] == farm_id, index_name] = value - with col2: - active_farms = int(len(farm_df["Farm_ID"].unique()) * 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) + # رتبه‌بندی بر اساس NDVI (به عنوان مثال) + if 'NDVI' in farm_ranks.columns: + farm_ranks['رتبه NDVI'] = farm_ranks['NDVI'].rank(ascending=False) - with col3: - avg_height = farm_df['CurrentHeight'].mean() - st.markdown('
', unsafe_allow_html=True) - st.markdown(f'
{avg_height:.1f} cm
', unsafe_allow_html=True) - st.markdown('
میانگین ارتفاع
', unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) + return farm_ranks + +# تابع اصلی برنامه +def main(): + # عنوان اصلی + st.title("داشبورد مانیتورینگ مزارع نیشکر دهخدا") - with col4: - avg_moisture = farm_df['CurrentMoisture'].mean() - st.markdown('
', unsafe_allow_html=True) - st.markdown(f'
{avg_moisture:.1f}%
', unsafe_allow_html=True) - st.markdown('
میانگین رطوبت
', unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) + # احراز هویت GEE + gee_initialized = initialize_gee() - # تب‌های داشبورد با انیمیشن - tab1, tab2, tab3, tab4 = st.tabs(["نمای کلی", "نقشه مزارع", "نمودارها", "داده‌ها"]) + if not gee_initialized: + st.error("اتصال به Google Earth Engine برقرار نشد. لطفاً فایل احراز هویت را بررسی کنید.") + return - with tab1: - st.markdown('
', unsafe_allow_html=True) - st.markdown("### توزیع واریته‌ها و سن محصول", unsafe_allow_html=True) - - col1, col2 = st.columns(2) - - with col1: - variety_counts = farm_df['Variety'].value_counts().reset_index() - variety_counts.columns = ['Variety', 'Count'] - fig = px.pie( - variety_counts, - values='Count', - names='Variety', - 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), - template="plotly_white", - height=400 - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) - - with col2: - age_counts = farm_df['Age'].value_counts().reset_index() - age_counts.columns = ['Age', 'Count'] - fig = px.pie( - age_counts, - values='Count', - names='Age', - 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), - template="plotly_white", - height=400 - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) - - st.markdown("### اطلاعات کلی مزارع") - - total_area = farm_df['Area'].sum() - - col1, col2, col3 = st.columns(3) - with col1: - st.markdown('
', unsafe_allow_html=True) - st.markdown(f'
{len(farm_df["Farm_ID"].unique())}
', unsafe_allow_html=True) - st.markdown('
تعداد کل مزارع
', unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) - with col2: - st.markdown('
', unsafe_allow_html=True) - st.markdown(f'
{total_area:.2f}
', unsafe_allow_html=True) - st.markdown('
مساحت کل (هکتار)
', unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) - with col3: - st.markdown('
', unsafe_allow_html=True) - st.markdown(f'
{farm_df["Channel"].nunique()}
', unsafe_allow_html=True) - st.markdown('
تعداد کانال‌ها
', unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) - - st.markdown('
', unsafe_allow_html=True) - - st_lottie(lottie_farm, height=350, key="farm_animation", speed=1.2) - st.markdown('
', unsafe_allow_html=True) + # خواندن داده‌ها + df = load_data() - with tab2: - st.markdown('
', unsafe_allow_html=True) - st.markdown("### نقشه مزارع", unsafe_allow_html=True) - - 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['Latitude'] - lon = farm['Longitude'] - name = farm['Farm_ID'] - farm_info = farm_df[farm_df['Farm_ID'] == name] - if not farm_info.empty: - variety = farm_info['Variety'].iloc[0] - age = farm_info['Age'].iloc[0] - area = farm_info['Area'].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='#2ecc71', icon='leaf', prefix='fa') - ).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("داده‌های مختصات در دسترس نیست.", icon="⚠️") - st.markdown('
', unsafe_allow_html=True) + if df is None: + st.error("خواندن داده‌ها با مشکل مواجه شد. لطفاً فایل CSV را بررسی کنید.") + return - with tab3: - st.markdown('
', unsafe_allow_html=True) - st.markdown("### نمودار رشد هفتگی", unsafe_allow_html=True) - - col1, col2 = st.columns(2) - with col1: - selected_variety = st.selectbox( - "انتخاب واریته", - ["all"] + list(farm_df['Variety'].unique()), - format_func=lambda x: "همه واریته‌ها" if x == "all" else x, - key="growth_variety", - help="واریته موردنظر را برای تحلیل انتخاب کنید" - ) - - with col2: - selected_age = st.selectbox( - "انتخاب سن", - ["all"] + list(farm_df['Age'].unique()), - format_func=lambda x: "همه سنین" if x == "all" else x, - key="growth_age", - help="سن موردنظر را برای تحلیل انتخاب کنید" - ) - - growth_data = generate_real_growth_data(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='#2ecc71', width=4), - marker=dict(size=10, color='#2ecc71') - )) - fig.update_layout( - title='میانگین رشد هفتگی', - xaxis_title='هفته', - yaxis_title='ارتفاع (سانتی‌متر)', - font=dict(family='Vazirmatn', size=16), - hovermode='x unified', - template='plotly_white', - height=500, - plot_bgcolor='#f5faff', - paper_bgcolor='#f5faff' - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) - - with chart_tab2: - if growth_data['individual']: - fig = go.Figure() - colors = ['#2ecc71', '#3498db', '#e67e22', '#9b59b6', '#e74c3c'] - 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=3), - marker=dict(size=8, color=colors[i % len(colors)]) - )) - fig.update_layout( - title='رشد هفتگی مزارع فردی', - xaxis_title='هفته', - yaxis_title='ارتفاع (سانتی‌متر)', - font=dict(family='Vazirmatn', size=16), - hovermode='x unified', - template='plotly_white', - height=500, - plot_bgcolor='#f5faff', - paper_bgcolor='#f5faff' - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) - else: - st.warning("داده‌ای برای نمایش وجود ندارد.", icon="⚠️") - st.markdown('
', unsafe_allow_html=True) + # تبدیل ستون‌های مختصات به عدد + df['طول جغرافیایی'] = pd.to_numeric(df['طول جغرافیایی'], errors='coerce') + df['عرض جغرافیایی'] = pd.to_numeric(df['عرض جغرافیایی'], errors='coerce') - with tab4: - st.markdown('
', unsafe_allow_html=True) - st.markdown("### داده‌های مزارع", unsafe_allow_html=True) - - search_term = st.text_input( - "جستجو در داده‌ها", - placeholder="نام مزرعه، واریته، سن و...", - help="عبارت موردنظر را برای جستجو وارد کنید" - ) - - if search_term: - filtered_df = farm_df[ - farm_df['Farm_ID'].astype(str).str.contains(search_term, case=False) | - farm_df['Variety'].astype(str).str.contains(search_term, case=False) | - farm_df['Age'].astype(str).str.contains(search_term, case=False) | - farm_df['Channel'].astype(str).str.contains(search_term, case=False) - ] - 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", - key="download_data", - help="داده‌های فیلترشده را دانلود کنید" - ) - st.dataframe( - filtered_df, - use_container_width=True, - height=450, - hide_index=True - ) - st.info(f"نمایش {len(filtered_df)} مزرعه از {len(farm_df)} مزرعه", icon="ℹ️") - else: - st.warning("هیچ داده‌ای یافت نشد.", icon="⚠️") - st.markdown('
', unsafe_allow_html=True) - -# صفحه نقشه -elif selected == "نقشه مزارع": - st.markdown('
', unsafe_allow_html=True) - st.markdown("## نقشه مزارع با شاخص‌های ماهواره‌ای", unsafe_allow_html=True) + # حذف ردیف‌های با مختصات نامعتبر + df = df[~((df['طول جغرافیایی'] == 0) | (df['عرض جغرافیایی'] == 0) | + df['طول جغرافیایی'].isna() | df['عرض جغرافیایی'].isna())] - col1, col2 = st.columns([1, 3]) + # ایجاد ستون‌های جدید برای نمایش بهتر + df['مزرعه_نمایشی'] = df['مزرعه'].astype(str) - with col1: - st.markdown('
', unsafe_allow_html=True) - st.markdown("### تنظیمات نقشه") - - selected_farm = st.selectbox( - "انتخاب مزرعه", - options=coordinates_df['Farm_ID'].tolist(), - index=0, - format_func=lambda x: f"مزرعه {x}", - help="مزرعه موردنظر را برای نمایش نقشه انتخاب کنید" - ) - - selected_date = st.date_input( - "انتخاب تاریخ", - value=datetime.now(), - format="YYYY-MM-DD", - help="تاریخی را برای تولید نقشه انتخاب کنید" - ) - - selected_layer = st.selectbox( - "انتخاب شاخص", - options=["NDVI", "NDMI", "EVI", "NDWI"], - format_func=lambda x: { - "NDVI": "شاخص پوشش گیاهی (NDVI)", - "NDMI": "شاخص رطوبت (NDMI)", - "EVI": "شاخص پیشرفته گیاهی (EVI)", - "NDWI": "شاخص آب (NDWI)" - }[x], - help="شاخص موردنظر را برای تحلیل انتخاب کنید" - ) - - generate_map = st.button( - "تولید نقشه", - type="primary", - use_container_width=True, - help="نقشه را با تنظیمات انتخاب‌شده تولید کنید" - ) + # ایجاد sidebar + st.sidebar.title("فیلترها و تنظیمات") + + # فیلتر روز هفته + days_of_week = df['روزهای هفته'].unique().tolist() + selected_day = st.sidebar.selectbox("انتخاب روز هفته", days_of_week) + + # فیلتر مزرعه + filtered_df = df[df['روزهای هفته'] == selected_day] + farm_ids = filtered_df['مزرعه'].unique().tolist() + selected_farm = st.sidebar.selectbox("انتخاب مزرعه", farm_ids) + + # فیلتر شاخص + indices = ['NDVI', 'EVI', 'NDMI', 'LAI', 'Biomass', 'MSI', 'Chlorophyll'] + selected_index = st.sidebar.selectbox("انتخاب شاخص", indices) + + # تنظیمات تاریخ + st.sidebar.subheader("بازه زمانی") + today = datetime.date.today() + start_date = st.sidebar.date_input("تاریخ شروع", today - datetime.timedelta(days=30)) + end_date = st.sidebar.date_input("تاریخ پایان", today) + + # تنظیمات نمایش نقشه + st.sidebar.subheader("تنظیمات نقشه") + map_zoom = st.sidebar.slider("بزرگنمایی نقشه", 10, 18, 13) + + # دکمه اعمال فیلترها + apply_filters = st.sidebar.button("اعمال فیلترها") + + # نمایش اطلاعات مزرعه انتخاب شده + if selected_farm: + farm_data = filtered_df[filtered_df['مزرعه'] == selected_farm].iloc[0] - st.markdown('
', unsafe_allow_html=True) + st.subheader(f"اطلاعات مزرعه {selected_farm}") - st.markdown("### راهنمای شاخص‌ها") + # نمایش اطلاعات در چند ستون + col1, col2, col3 = st.columns(3) - 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) - """, unsafe_allow_html=True) + with col1: + st.markdown(f"**کانال:** {farm_data['کانال']}") + st.markdown(f"**اداره:** {farm_data['اداره']}") + st.markdown(f"**مساحت داشت:** {farm_data['مساحت داشت']} هکتار") - 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) - """, unsafe_allow_html=True) + with col2: + st.markdown(f"**واریته:** {farm_data['واریته']}") + st.markdown(f"**سن:** {farm_data['سن']}") + st.markdown(f"**روز هفته:** {farm_data['روزهای هفته']}") - with st.expander("شاخص پیشرفته گیاهی (EVI)", expanded=selected_layer == "EVI"): - st.markdown(""" - **شاخص پیشرفته پوشش گیاهی (EVI)** نسخه بهبودیافته NDVI است که حساسیت کمتری به اثرات خاک و اتمسفر دارد. + with col3: + st.markdown(f"**طول جغرافیایی:** {farm_data['طول جغرافیایی']}") + st.markdown(f"**عرض جغرافیایی:** {farm_data['عرض جغرافیایی']}") + + # ایجاد تب‌ها برای نمایش نقشه و نمودارها + tab1, tab2, tab3 = st.tabs(["نقشه", "نمودارها", "جدول مقایسه"]) + + with tab1: + if apply_filters and selected_farm: + # دریافت مختصات مزرعه انتخاب شده + farm_data = filtered_df[filtered_df['مزرعه'] == selected_farm].iloc[0] + center_lat = farm_data['عرض جغرافیایی'] + center_lon = farm_data['طول جغرافیایی'] - - **مقادیر بالا (0.4 تا 1.0)**: پوشش گیاهی متراکم و سالم - - **مقادیر متوسط (0.2 تا 0.4)**: پوشش گیاهی متوسط - - **مقادیر پایین (0.0 تا 0.2)**: پوشش گیاهی کم + # ایجاد منطقه مورد نظر (AOI) - یک بافر 500 متری اطراف نقطه مرکزی مزرعه + point = ee.Geometry.Point([center_lon, center_lat]) + aoi = point.buffer(500) - فرمول: EVI = 2.5 * ((NIR - RED) / (NIR + 6*RED - 7.5*BLUE + 1)) - """, unsafe_allow_html=True) - - with st.expander("شاخص آب (NDWI)", expanded=selected_layer == "NDWI"): - st.markdown(""" - **شاخص تفاضل نرمال‌شده آب (NDWI)** برای شناسایی پهنه‌های آبی و ارزیابی محتوای آب در گیاهان استفاده می‌شود. + # ایجاد نقشه + m = create_map(center_lat, center_lon, map_zoom) - - **مقادیر بالا (0.3 تا 1.0)**: پهنه‌های آبی - - **مقادیر متوسط (0.0 تا 0.3)**: محتوای آب متوسط - - **مقادیر پایین (-1.0 تا 0.0)**: محتوای آب کم یا خاک خشک + # دریافت تصاویر Sentinel-2 + imagery = get_sentinel_imagery(start_date.strftime('%Y-%m-%d'), + end_date.strftime('%Y-%m-%d'), + aoi) - فرمول: NDWI = (GREEN - NIR) / (GREEN + NIR) - """, unsafe_allow_html=True) - - st.markdown('
', unsafe_allow_html=True) - - 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=900, height=600) - st.success(f"نقشه {selected_layer} برای مزرعه {selected_farm} با موفقیت تولید شد.", icon="✅") - else: - st.error("خطا در تولید نقشه. لطفاً دوباره تلاش کنید.", icon="❌") - elif 'last_map' in st.session_state: - folium_static(st.session_state.last_map, width=900, height=600) - st.markdown('
', unsafe_allow_html=True) - st.info(""" - **نکته:** این نقشه بر اساس تصاویر ماهواره‌ای Sentinel-2 تولید شده است. - برای دقت بیشتر، تاریخی را انتخاب کنید که ابرناکی کمتری داشته باشد. - """, icon="ℹ️") - - with stats_tab: - if 'last_map' in st.session_state: - stats = calculate_farm_stats(selected_farm, selected_layer) + if imagery is not None: + # تنظیم پارامترهای نمایش برای شاخص انتخاب شده + vis_params = { + 'NDVI': {'min': -0.2, 'max': 0.8, 'palette': ['red', 'yellow', 'green']}, + 'EVI': {'min': -0.2, 'max': 1, 'palette': ['red', 'yellow', 'green']}, + 'NDMI': {'min': -0.2, 'max': 0.8, 'palette': ['red', 'yellow', 'blue']}, + 'LAI': {'min': 0, 'max': 5, 'palette': ['white', 'lightgreen', 'darkgreen']}, + 'Biomass': {'min': 0, 'max': 5, 'palette': ['white', 'lightgreen', 'darkgreen']}, + 'MSI': {'min': 0, 'max': 2, 'palette': ['blue', 'yellow', 'red']}, + 'Chlorophyll': {'min': 0, 'max': 5, 'palette': ['white', 'yellow', 'green']} + } - col1, col2, col3, col4 = st.columns(4) + # اضافه کردن لایه شاخص به نقشه + m = add_index_layer(m, imagery, selected_index, vis_params[selected_index], f"شاخص {selected_index}") - with col1: - st.markdown('
', unsafe_allow_html=True) - st.markdown(f'
{stats["mean"]:.2f}
', unsafe_allow_html=True) - st.markdown(f'
میانگین {selected_layer}
', unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) + # اضافه کردن نقاط مزارع به نقشه + for _, row in filtered_df.iterrows(): + folium.Marker( + location=[row['عرض جغرافیایی'], row['طول جغرافیایی']], + popup=f"مزرعه: {row['مزرعه']}
واریته: {row['واریته']}
سن: {row['سن']}", + tooltip=f"مزرعه {row['مزرعه']}", + icon=folium.Icon(color='blue' if row['مزرعه'] != selected_farm else 'red') + ).add_to(m) - with col2: - st.markdown('
', unsafe_allow_html=True) - st.markdown(f'
{stats["max"]:.2f}
', unsafe_allow_html=True) - st.markdown(f'
حداکثر {selected_layer}
', unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) + # اضافه کردن کنترل‌های نقشه + m.add_control(Draw(export=True)) + m.add_control(Fullscreen()) + m.add_control(MeasureControl()) - with col3: - st.markdown('
', unsafe_allow_html=True) - st.markdown(f'
{stats["min"]:.2f}
', unsafe_allow_html=True) - st.markdown(f'
حداقل {selected_layer}
', unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) + # نمایش نقشه + folium_static(m) - with col4: - st.markdown('
', unsafe_allow_html=True) - st.markdown(f'
{stats["std_dev"]:.2f}
', unsafe_allow_html=True) - st.markdown(f'
انحراف معیار
', unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) + # دکمه دانلود نقشه + st.markdown(get_map_download_link(m), 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=["#2ecc71"] - ) - fig.update_layout( - font=dict(family="Vazirmatn", size=14), - template="plotly_white", - bargap=0.1, - plot_bgcolor='#f5faff', - paper_bgcolor='#f5faff', - height=400 - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) + # نمایش راهنمای رنگ‌ها + st.subheader("راهنمای رنگ‌ها") - dates = pd.date_range(end=selected_date, periods=30, freq='D') - values = [stats["mean"] + np.random.normal(0, stats["std_dev"] / 2) for _ in range(30)] - values = np.clip(values, stats["min"], stats["max"]) + if selected_index in ['NDVI', 'EVI']: + st.markdown(""" + - قرمز: وضعیت ضعیف (0.0-0.2) + - زرد: وضعیت متوسط (0.2-0.5) + - سبز: وضعیت خوب (0.5-0.8) + """, unsafe_allow_html=True) + elif selected_index == 'NDMI': + st.markdown(""" + - قرمز: خشک (0.0-0.2) + - زرد: رطوبت متوسط (0.2-0.5) + - آبی: رطوبت بالا (0.5-0.8) + """, unsafe_allow_html=True) + elif selected_index in ['LAI', 'Biomass']: + st.markdown(""" + - سفید: مقدار کم (0-1) + - سبز روشن: مقدار متوسط (1-3) + - سبز تیره: مقدار زیاد (3-5) + """, unsafe_allow_html=True) - fig = px.line( - x=dates, - y=values, - title=f"روند تغییرات {selected_layer} در 30 روز گذشته", - labels={"x": "تاریخ", "y": f"مقدار {selected_layer}"}, - markers=True, - line_shape="spline" - ) - fig.update_layout( - font=dict(family="Vazirmatn", size=14), - template="plotly_white", - hovermode="x unified", - plot_bgcolor='#f5faff', - paper_bgcolor='#f5faff', - height=400 - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) + # محاسبه میانگین شاخص برای مزرعه انتخاب شده + mean_value = imagery.select(selected_index).reduceRegion( + reducer=ee.Reducer.mean(), + geometry=aoi, + scale=10 + ).get(selected_index).getInfo() - farm_names = coordinates_df['Farm_ID'].tolist()[:5] - comparison_values = [stats["mean"] + np.random.uniform(-0.2, 0.2) for _ in range(len(farm_names))] - - fig = px.bar( - x=farm_names, - y=comparison_values, - title=f"مقایسه {selected_layer} بین مزارع", - labels={"x": "مزرعه", "y": f"مقدار {selected_layer}"}, - color=comparison_values, - color_continuous_scale="Viridis" - ) - fig.update_layout( - font=dict(family="Vazirmatn", size=14), - template="plotly_white", - coloraxis_showscale=False, - plot_bgcolor='#f5faff', - paper_bgcolor='#f5faff', - height=400 - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) + if mean_value is not None: + st.metric(f"میانگین {selected_index} مزرعه {selected_farm}", f"{mean_value:.4f}") else: - st.warning("لطفاً ابتدا یک نقشه تولید کنید.", icon="⚠️") - st.markdown('
', unsafe_allow_html=True) - -# صفحه ورود اطلاعات -elif selected == "ورود اطلاعات": - st.markdown('
', unsafe_allow_html=True) - st.markdown("## ورود اطلاعات روزانه مزارع", unsafe_allow_html=True) - - tab1, tab2 = st.tabs(["ورود دستی", "آپلود فایل"]) + st.warning("تصویر ماهواره‌ای مناسبی برای بازه زمانی انتخاب شده یافت نشد. لطفاً بازه زمانی دیگری را انتخاب کنید.") - 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}", - help="هفته موردنظر را برای ورود اطلاعات انتخاب کنید" - ) - - with col2: - days = day_df['Day'].unique().tolist() - selected_day = st.selectbox("انتخاب روز", options=days, help="روز موردنظر را برای ورود اطلاعات انتخاب کنید") - - filtered_farms = farm_df[farm_df['Week'] == int(selected_week)] - filtered_farms = filtered_farms[filtered_farms['Farm_ID'].isin(day_df[day_df['Day'] == selected_day]['Farm_ID'])] - - if filtered_farms.empty: - st.warning(f"هیچ مزرعه‌ای برای هفته {selected_week} و روز {selected_day} در پایگاه داده وجود ندارد.", icon="⚠️") - 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({ - 'Farm_ID': filtered_farms['Farm_ID'], - 'Station1': [0] * len(filtered_farms), - 'Station2': [0] * len(filtered_farms), - 'Station3': [0] * len(filtered_farms), - 'Station4': [0] * len(filtered_farms), - 'Station5': [0] * len(filtered_farms), - 'Well1': [0] * len(filtered_farms), - 'Well2': [0] * len(filtered_farms), - 'CurrentMoisture': [0] * len(filtered_farms), - 'CurrentNitrogen': [0] * len(filtered_farms), - 'CurrentHeight': [0] * len(filtered_farms) - }) + with tab2: + if apply_filters and selected_farm: + # دریافت مختصات مزرعه انتخاب شده + farm_data = filtered_df[filtered_df['مزرعه'] == selected_farm].iloc[0] + center_lat = farm_data['عرض جغرافیایی'] + center_lon = farm_data['طول جغرافیایی'] - edited_df = st.data_editor( - st.session_state[data_key], - use_container_width=True, - num_rows="fixed", - column_config={ - "Farm_ID": st.column_config.TextColumn("مزرعه", disabled=True), - "Station1": st.column_config.NumberColumn("ایستگاه 1", min_value=0, max_value=400, step=1, help="ارتفاع ایستگاه 1 را وارد کنید"), - "Station2": st.column_config.NumberColumn("ایستگاه 2", min_value=0, max_value=400, step=1, help="ارتفاع ایستگاه 2 را وارد کنید"), - "Station3": st.column_config.NumberColumn("ایستگاه 3", min_value=0, max_value=400, step=1, help="ارتفاع ایستگاه 3 را وارد کنید"), - "Station4": st.column_config.NumberColumn("ایستگاه 4", min_value=0, max_value=400, step=1, help="ارتفاع ایستگاه 4 را وارد کنید"), - "Station5": st.column_config.NumberColumn("ایستگاه 5", min_value=0, max_value=400, step=1, help="ارتفاع ایستگاه 5 را وارد کنید"), - "Well1": st.column_config.NumberColumn("چاهک 1", min_value=0, max_value=200, step=1, help="مقدار چاهک 1 را وارد کنید"), - "Well2": st.column_config.NumberColumn("چاهک 2", min_value=0, max_value=200, step=1, help="مقدار چاهک 2 را وارد کنید"), - "CurrentMoisture": st.column_config.NumberColumn("رطوبت", min_value=0, max_value=100, step=0.1, help="رطوبت فعلی را وارد کنید"), - "CurrentNitrogen": st.column_config.NumberColumn("نیتروژن", min_value=0, max_value=5, step=0.01, help="نیتروژن فعلی را وارد کنید"), - "CurrentHeight": st.column_config.NumberColumn("میانگین ارتفاع", disabled=True, help="میانگین ارتفاع به‌طور خودکار محاسبه می‌شود") - }, - hide_index=True - ) + # ایجاد منطقه مورد نظر (AOI) + point = ee.Geometry.Point([center_lon, center_lat]) + aoi = point.buffer(500) - for i in range(len(edited_df)): - stations = [ - edited_df.iloc[i]['Station1'], - edited_df.iloc[i]['Station2'], - edited_df.iloc[i]['Station3'], - edited_df.iloc[i]['Station4'], - edited_df.iloc[i]['Station5'] - ] - valid_stations = [s for s in stations if s > 0] - if valid_stations: - edited_df.iloc[i, edited_df.columns.get_loc('CurrentHeight')] = round(sum(valid_stations) / len(valid_stations), 1) + # انتخاب بازه زمانی برای نمودار + time_interval = st.radio("انتخاب بازه زمانی", ["روزانه", "هفتگی", "ماهانه"], horizontal=True) + interval_map = {"روزانه": "day", "هفتگی": "week", "ماهانه": "month"} - st.session_state[data_key] = edited_df + # ایجاد نمودار زمانی + time_series_df = create_time_series(aoi, selected_index, start_date, end_date, interval_map[time_interval]) - if st.button("ذخیره اطلاعات", type="primary", use_container_width=True, help="داده‌ها را ذخیره کنید"): - new_data = edited_df.copy() - new_data['Week'] = int(selected_week) - new_data['Measurement_Date'] = (datetime.now() - timedelta(weeks=(22 - int(selected_week)))).strftime('%Y-%m-%d') - new_data['Variety'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Variety']) - new_data['Age'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Age']) - new_data['Area'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Area']) - new_data['Channel'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Channel']) - new_data['Administration'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Administration']) + if time_series_df is not None and not time_series_df.empty: + fig = px.line(time_series_df, x='Date', y=selected_index, + title=f"نمودار زمانی {selected_index} برای مزرعه {selected_farm}") + fig.update_layout(xaxis_title="تاریخ", yaxis_title=selected_index) + st.plotly_chart(fig, use_container_width=True) - st.session_state.heights_df = pd.concat([st.session_state.heights_df, new_data], ignore_index=True) - st.success(f"داده‌های هفته {selected_week} برای روز {selected_day} با موفقیت ذخیره شدند.", icon="✅") - st.balloons() - - with tab2: - st.markdown("### آپلود فایل اکسل") - - uploaded_file = st.file_uploader( - "فایل اکسل یا CSV خود را آپلود کنید", - type=["xlsx", "xls", "csv"], - help="فایل داده‌های مزارع را آپلود کنید" - ) - - 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) + # نمایش داده‌های نمودار + st.dataframe(time_series_df) - if st.button("ذخیره فایل", type="primary", use_container_width=True, help="فایل را ذخیره کنید"): - st.session_state.heights_df = pd.concat([st.session_state.heights_df, df], ignore_index=True) - st.success("فایل با موفقیت ذخیره شد.", icon="✅") - st.balloons() - except Exception as e: - st.error(f"خطا در خواندن فایل: {e}", icon="❌") - - st.markdown("### راهنمای فرمت فایل") - st.markdown(""" - فایل اکسل یا CSV باید شامل ستون‌های زیر باشد: - - - Farm_ID - - Station1 تا Station5 - - Well1 و Well2 - - CurrentMoisture (رطوبت) - - CurrentNitrogen - - می‌توانید از [این فایل نمونه](https://example.com/sample.xlsx) به عنوان الگو استفاده کنید. - """, unsafe_allow_html=True) - - st.markdown(""" -
- - - - - -

فایل خود را اینجا رها کنید یا روی دکمه بالا کلیک کنید

-
- """, unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) - -# صفحه تحلیل داده‌ها -elif selected == "تحلیل داده‌ها": - st.markdown('
', unsafe_allow_html=True) - st.markdown("## تحلیل هوشمند داده‌ها", unsafe_allow_html=True) - - col1, col2 = st.columns([1, 2]) - - with col1: - st_lottie(lottie_analysis, height=250, key="analysis_animation", speed=1.2) - - with col2: - st.markdown(""" -
-

تحلیل پیشرفته داده‌های مزارع

-

در این بخش می‌توانید تحلیل‌های پیشرفته روی داده‌های مزارع انجام دهید و روندها و الگوهای مختلف را بررسی کنید.

-
- """, unsafe_allow_html=True) - - tab1, tab2, tab3, tab4 = st.tabs(["تحلیل رشد", "مقایسه واریته‌ها", "تحلیل رطوبت", "پیش‌بینی"]) - - with tab1: - st.markdown("### تحلیل رشد مزارع") - - col1, col2 = st.columns(2) - - with col1: - selected_variety = st.selectbox( - "انتخاب واریته", - ["all"] + list(farm_df['Variety'].unique()), - format_func=lambda x: "همه واریته‌ها" if x == "all" else x, - key="growth_variety", - help="واریته موردنظر را برای تحلیل رشد انتخاب کنید" - ) - - with col2: - selected_age = st.selectbox( - "انتخاب سن", - ["all"] + list(farm_df['Age'].unique()), - format_func=lambda x: "همه سنین" if x == "all" else x, - key="growth_age", - help="سن موردنظر را برای تحلیل رشد انتخاب کنید" - ) - - growth_data = generate_real_growth_data(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=450, - 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)): - if heights[i] > 0 and heights[i-1] > 0: - 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=450, - title='نرخ رشد هفتگی مزارع' - ).interactive() - - st.altair_chart(chart, use_container_width=True) - else: - st.warning("داده‌ای برای نمایش وجود ندارد.", icon="⚠️") - - with tab2: - st.markdown("### مقایسه واریته‌ها") - - variety_age_groups = farm_df.groupby(['Variety', 'Age']).size().reset_index(name='Count') - - fig = px.density_heatmap( - variety_age_groups, - x='Variety', - y='Age', - z='Count', - title='توزیع مزارع بر اساس واریته و سن', - color_continuous_scale='Viridis' - ) - fig.update_layout( - font=dict(family="Vazirmatn", size=14), - template="plotly_white", - xaxis_title="واریته", - yaxis_title="سن", - plot_bgcolor='#f5faff', - paper_bgcolor='#f5faff', - height=450 - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) - - variety_heights = farm_df.groupby('Variety')['CurrentHeight'].apply(list).to_dict() - - fig = go.Figure() - for variety, heights in variety_heights.items(): - fig.add_trace(go.Box( - y=heights, - name=variety, - boxpoints='outliers', - marker_color=f'hsl({hash(variety) % 360}, 70%, 50%)' - )) - fig.update_layout( - title='مقایسه ارتفاع بر اساس واریته', - yaxis_title='ارتفاع (سانتی‌متر)', - font=dict(family="Vazirmatn", size=14), - template="plotly_white", - boxmode='group', - plot_bgcolor='#f5faff', - paper_bgcolor='#f5faff', - height=450 - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) - - variety_stats = {} - for variety, heights in variety_heights.items(): - 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) + # دکمه دانلود داده‌های نمودار + csv = time_series_df.to_csv(index=False) + b64 = base64.b64encode(csv.encode()).decode() + href = f'دانلود داده‌های نمودار' + st.markdown(href, unsafe_allow_html=True) + else: + st.warning("داده‌ای برای نمایش نمودار زمانی یافت نشد. لطفاً بازه زمانی دیگری را انتخاب کنید.") with tab3: - st.markdown("### تحلیل رطوبت مزارع") - - farms = farm_df['Farm_ID'].unique()[:10] - dates = pd.date_range(end=datetime.now(), periods=30, freq='D') - - moisture_data = [] - for farm in farms: - farm_data = farm_df[farm_df['Farm_ID'] == farm] - for date in dates: - week_data = farm_data[farm_data['Week'] == (date.isocalendar()[1] % 23 + 1)] - moisture = week_data['CurrentMoisture'].mean() if not week_data.empty else np.random.uniform(50, 80) - 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", size=14), - template="plotly_white", - hovermode="x unified", - plot_bgcolor='#f5faff', - paper_bgcolor='#f5faff', - height=450 - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) - - st.markdown("### همبستگی رطوبت و ارتفاع") - - correlation_data = [] - for farm in farms: - farm_data = farm_df[farm_df['Farm_ID'] == farm] - for _, row in farm_data.iterrows(): - correlation_data.append({ - 'Farm': farm, - 'Moisture': row['CurrentMoisture'], - 'Height': row['CurrentHeight'] - }) - - 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", size=14), - template="plotly_white", - plot_bgcolor='#f5faff', - paper_bgcolor='#f5faff', - height=450 - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) - - correlation = correlation_df['Moisture'].corr(correlation_df['Height']) - st.info(f"ضریب همبستگی بین رطوبت و ارتفاع: {correlation:.2f}", icon="ℹ️") - - with tab4: - st.markdown("### پیش‌بینی رشد مزارع") - - selected_farm_for_prediction = st.selectbox( - "انتخاب مزرعه", - options=farm_df['Farm_ID'].tolist(), - format_func=lambda x: f"مزرعه {x}", - help="مزرعه موردنظر را برای پیش‌بینی رشد انتخاب کنید" - ) - - farm_data = farm_df[farm_df['Farm_ID'] == selected_farm_for_prediction] - historical_weeks = farm_data['Week'].values - historical_heights = farm_data['CurrentHeight'].values - - if len(historical_weeks) > 1 and len(historical_heights) > 1: - model = LinearRegression() - model.fit(historical_weeks.reshape(-1, 1), historical_heights) + if apply_filters: + st.subheader("مقایسه مزارع") - future_weeks = np.array(range(max(historical_weeks) + 1, 30)).reshape(-1, 1) - future_heights = model.predict(future_weeks) - lower_bound = future_heights - 15 - upper_bound = future_heights + 15 + # انتخاب مزارع برای مقایسه + farms_to_compare = st.multiselect("انتخاب مزارع برای مقایسه", farm_ids, default=[selected_farm] if selected_farm else []) - fig = go.Figure() - fig.add_trace(go.Scatter( - x=historical_weeks, - y=historical_heights, - mode='lines+markers', - name='داده‌های تاریخی', - line=dict(color='#2ecc71', width=4), - marker=dict(size=10, color='#2ecc71') - )) - fig.add_trace(go.Scatter( - x=future_weeks.flatten(), - y=future_heights, - mode='lines', - name='پیش‌بینی', - line=dict(color='#3498db', width=3, dash='dash') - )) - fig.add_trace(go.Scatter( - x=future_weeks.flatten(), - y=lower_bound, - mode='lines', - name='حد پایین', - line=dict(color='#e74c3c', width=2, dash='dot'), - showlegend=True - )) - fig.add_trace(go.Scatter( - x=future_weeks.flatten(), - y=upper_bound, - mode='lines', - name='حد بالا', - line=dict(color='#e74c3c', width=2, dash='dot'), - fill='tonexty', - showlegend=True - )) - fig.update_layout( - title=f'پیش‌بینی رشد مزرعه {selected_farm_for_prediction}', - xaxis_title='هفته', - yaxis_title='ارتفاع (سانتی‌متر)', - font=dict(family='Vazirmatn', size=16), - hovermode='x unified', - template='plotly_white', - height=500, - plot_bgcolor='#f5faff', - paper_bgcolor='#f5faff' - ) - st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False}) - else: - st.warning("داده‌های کافی برای پیش‌بینی وجود ندارد.", icon="⚠️") - st.markdown('
', unsafe_allow_html=True) - -# صفحه گزارش‌گیری -elif selected == "گزارش‌گیری": - st.markdown('
', unsafe_allow_html=True) - st.markdown("## گزارش‌گیری", unsafe_allow_html=True) - - report_week = st.selectbox( - "انتخاب هفته برای گزارش", - options=[str(i) for i in range(1, 23)], - help="هفته موردنظر را برای گزارش انتخاب کنید" - ) - report_day = st.selectbox( - "انتخاب روز برای گزارش", - options=day_df['Day'].unique().tolist(), - help="روز موردنظر را برای گزارش انتخاب کنید" - ) - - report_df = st.session_state.heights_df[ - (st.session_state.heights_df['Week'] == int(report_week)) & - (st.session_state.heights_df['Farm_ID'].isin(day_df[day_df['Day'] == report_day]['Farm_ID'])) - ] - - if not report_df.empty: - st.markdown(f"### گزارش هفته {report_week} - روز {report_day}") - st.dataframe(report_df, use_container_width=True) - - csv = report_df.to_csv(index=False).encode('utf-8') - st.download_button( - label="دانلود گزارش (CSV)", - data=csv, - file_name=f"report_week_{report_week}_day_{report_day}.csv", - mime="text/csv", - key="download_report", - help="گزارش را به صورت فایل CSV دانلود کنید" - ) - - st_lottie(lottie_report, height=250, key="report_animation", speed=1.2) - else: - st.warning(f"داده‌ای برای هفته {report_week} و روز {report_day} یافت نشد.", icon="⚠️") - st.markdown('
', unsafe_allow_html=True) - -# صفحه تنظیمات -elif selected == "تنظیمات": - st.markdown('
', unsafe_allow_html=True) - st.markdown("## تنظیمات سامانه", unsafe_allow_html=True) - - st.markdown(""" -
-

تنظیمات پیشرفته

-

در این بخش می‌توانید تنظیمات کلی سامانه، از جمله به‌روزرسانی داده‌ها و پیکربندی‌های ظاهری را مدیریت کنید.

-
- """, unsafe_allow_html=True) - - st.markdown("### به‌روزرسانی داده‌ها") - - if st.button("بارگذاری مجدد داده‌ها", type="primary", use_container_width=True, help="داده‌ها را دوباره بارگذاری کنید"): - st.session_state.heights_df = load_farm_data() - st.success("داده‌ها با موفقیت به‌روزرسانی شدند.", icon="✅") - - st.markdown("### تنظیمات ظاهری") - - theme = st.radio( - "انتخاب تم", - options=["سبز براق (پیش‌فرض)", "آبی کریستالی", "طلایی کلاسیک"], - format_func=lambda x: x, - help="تم موردنظر را برای رابط کاربری انتخاب کنید" - ) - - if theme == "آبی کریستالی": - st.markdown(""" - - """, unsafe_allow_html=True) - elif theme == "طلایی کلاسیک": - st.markdown(""" - - """, unsafe_allow_html=True) - - st.markdown("### اطلاعات تماس") - st.markdown(""" -
-

برای پشتیبانی یا مشکلات فنی، با ما تماس بگیرید:

-

ایمیل: support@dehkhoda.com

-

تلفن: +98 21 12345678

-
- """, unsafe_allow_html=True) - st.markdown('
', unsafe_allow_html=True) + if farms_to_compare: + # ایجاد دیکشنری برای ذخیره مقادیر شاخص‌ها + index_values = {} + + # محاسبه مقادیر شاخص‌ها برای هر مزرعه + for farm_id in farms_to_compare: + farm_data = filtered_df[filtered_df['مزرعه'] == farm_id].iloc[0] + center_lat = farm_data['عرض جغرافیایی'] + center_lon = farm_data['طول جغرافیایی'] + + point = ee.Geometry.Point([center_lon, center_lat]) + aoi = point.buffer(500) + + imagery = get_sentinel_imagery(start_date.strftime('%Y-%m-%d'), + end_date.strftime('%Y-%m-%d'), + aoi) + + if imagery is not None: + # محاسبه میانگین شاخص‌ها + values = {} + for index_name in indices: + mean_value = imagery.select(index_name).reduceRegion( + reducer=ee.Reducer.mean(), + geometry=aoi, + scale=10 + ).get(index_name).getInfo() + + if mean_value is not None: + values[index_name] = mean_value + + index_values[farm_id] = values + + # ایجاد دیتافریم برای مقایسه + comparison_data = [] + for farm_id in farms_to_compare: + if farm_id in index_values: + row = {'مزرعه': farm_id} + row.update(index_values[farm_id]) + comparison_data.append(row) + + if comparison_data: + comparison_df = pd.DataFrame(comparison_data) + + # نمایش جدول مقایسه + st.dataframe(comparison_df) + + # ایجاد نمودار مقایسه‌ای + if len(comparison_df) > 1: # اگر بیش از یک مزرعه انتخاب شده باشد + fig = go.Figure() + + for index_name in indices: + if index_name in comparison_df.columns: + fig.add_trace(go.Bar( + x=comparison_df['مزرعه'], + y=comparison_df[index_name], + name=index_name + )) + + fig.update_layout( + title="مقایسه شاخص‌ها بین مزارع", + xaxis_title="مزرعه", + yaxis_title="مقدار شاخص", + barmode='group' + ) + + st.plotly_chart(fig, use_container_width=True) + + # دکمه دانلود داده‌های مقایسه + csv = comparison_df.to_csv(index=False) + b64 = base64.b64encode(csv.encode()).decode() + href = f'دانلود داده‌های مقایسه' + st.markdown(href, unsafe_allow_html=True) + else: + st.warning("داده‌ای برای مقایسه مزارع یافت نشد.") + else: + st.info("لطفاً حداقل یک مزرعه را برای مقایسه انتخاب کنید.") -# پاورقی -st.markdown(""" - -""", unsafe_allow_html=True) \ No newline at end of file +# اجرای برنامه +if __name__ == "__main__": + main() \ No newline at end of file