File size: 24,534 Bytes
dae6b2b
8445b55
5b981a3
cfba302
0d7fb21
8445b55
 
 
 
 
 
 
 
 
7ba07c5
 
8445b55
 
7ba07c5
 
 
 
8445b55
 
 
 
7ba07c5
 
 
8445b55
 
7ba07c5
8445b55
 
7ba07c5
8445b55
 
7ba07c5
8445b55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7ba07c5
8445b55
 
 
 
 
 
 
 
 
 
7ba07c5
8445b55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7ba07c5
 
8445b55
 
 
7ba07c5
 
 
 
 
8445b55
 
 
 
 
7ba07c5
 
8445b55
 
 
 
7ba07c5
8445b55
 
 
 
7ba07c5
 
 
 
 
 
 
 
 
 
8445b55
 
7ba07c5
8445b55
 
7ba07c5
 
8445b55
 
 
7ba07c5
 
 
 
 
 
 
 
 
 
8445b55
7ba07c5
 
 
 
 
 
 
 
 
8445b55
7ba07c5
 
 
 
 
 
 
 
8445b55
7ba07c5
8445b55
 
7ba07c5
8445b55
 
 
 
 
 
 
7ba07c5
8445b55
 
7ba07c5
8445b55
 
 
 
 
7ba07c5
 
 
 
 
 
 
 
 
8445b55
 
 
 
 
 
 
 
7ba07c5
8445b55
 
 
 
 
 
 
 
 
09fd0b6
8445b55
 
 
7ba07c5
8445b55
 
 
 
 
 
 
7ba07c5
 
 
 
 
 
8445b55
7ba07c5
8445b55
7ba07c5
8445b55
7ba07c5
 
 
 
 
 
 
 
 
 
 
 
 
8445b55
 
 
 
 
 
 
 
 
 
 
 
 
7ba07c5
8445b55
 
7ba07c5
8445b55
 
 
 
 
 
 
 
 
 
 
7ba07c5
8445b55
 
 
 
 
 
 
 
 
 
 
 
7ba07c5
 
 
8445b55
 
 
 
 
7ba07c5
8445b55
 
 
 
 
 
7ba07c5
8445b55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7ba07c5
 
8445b55
 
7ba07c5
 
8445b55
7ba07c5
 
8445b55
 
7ba07c5
 
8445b55
 
7ba07c5
0d7fb21
8445b55
 
 
 
 
 
 
 
 
 
7ba07c5
 
 
 
 
8445b55
 
 
7ba07c5
8445b55
7ba07c5
 
 
 
 
8445b55
 
7ba07c5
8445b55
7ba07c5
 
8445b55
 
 
7ba07c5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
import streamlit as st
import ee
import pandas as pd
import folium
from streamlit_folium import st_folium
import os
from datetime import datetime, timedelta

# --- پیکربندی ---
SERVICE_ACCOUNT_FILE = 'ee-esmaeilkiani13877-cfdea6eaf411 (4).json' # مسیر فایل کلید خود را وارد کنید
# KHUZESTAN_ROI = ee.Geometry.Rectangle([47.5, 30.0, 50.5, 32.5]) # دیگر استفاده نمی‌شود اگر محاسبات بر اساس هندسه مزرعه است
@st.cache_resource
def initialize_gee():
    try:
        # اگر ee_initialized هنوز در وضعیت جلسه نیست یا مقدار آن False است، سعی در مقداردهی اولیه کن
        if not st.session_state.get('ee_initialized', False):
            if not os.path.exists(SERVICE_ACCOUNT_FILE):
                st.error(f"خطا: فایل Service Account در مسیر '{SERVICE_ACCOUNT_FILE}' یافت نشد.")
                st.session_state.ee_initialized = False
                # st.stop() # در صورت نیاز به توقف کامل برنامه
                return False # بازگشت وضعیت ناموفق

            credentials = ee.ServiceAccountCredentials(None, key_file=SERVICE_ACCOUNT_FILE)
            ee.Initialize(credentials=credentials, opt_url='https://earthengine-highvolume.googleapis.com')
            st.session_state.ee_initialized = True
            st.success("اتصال به Google Earth Engine با موفقیت انجام شد.")
        
        return st.session_state.get('ee_initialized', False) # بازگشت وضعیت فعلی مقداردهی اولیه

    except Exception as e:
        st.error(f"خطا در اتصال به GEE: {e}")
        st.session_state.ee_initialized = False # اطمینان از تنظیم وضعیت در صورت بروز هرگونه خطا
        return False

# --- بارگذاری داده‌های مزارع از GEE (بدون تغییر نسبت به نسخه قبلی شما) ---
@st.cache_data(show_spinner="در حال بارگذاری داده‌های مزارع از GEE...")
def load_farm_data_from_gee():
    if not st.session_state.get('ee_initialized', False): # بررسی صریح مقدار True
        st.warning("GEE مقداردهی اولیه نشده است.")
        return pd.DataFrame()

    try:
        farms_fc = ee.FeatureCollection("projects/ee-esmaeilkiani13877/assets/Croplogging-Farm")
        features = farms_fc.getInfo()['features']
        farm_records = []
        for f in features:
            props = f['properties']
            geom = f['geometry']
            ee_geom = None
            if geom['type'] == 'Polygon':
                ee_geom = ee.Geometry.Polygon(geom['coordinates'])
            elif geom['type'] == 'MultiPolygon':
                 ee_geom = ee.Geometry.MultiPolygon(geom['coordinates'])

            centroid_lon, centroid_lat = None, None
            if ee_geom:
                try:
                    centroid = ee_geom.centroid(maxError=1).getInfo()['coordinates']
                    centroid_lon, centroid_lat = centroid[0], centroid[1]
                except Exception:
                    pass

            area_ha = None
            if ee_geom:
                try:
                    area_m2 = ee_geom.area(maxError=1).getInfo()
                    if area_m2 is not None:
                        area_ha = area_m2 / 10000.0
                except Exception:
                    area_ha = props.get('Area')
            else:
                area_ha = props.get('Area')


            farm_records.append({
                'مزرعه': props.get('farm', ''),
                'گروه': props.get('group', ''),
                'واریته': props.get('Variety', ''),
                'سن': props.get('Age', ''),
                'مساحت (هکتار)': area_ha,
                'روز': props.get('Day', ''),
                'Field': props.get('Field', ''),
                'ee_geometry': ee_geom,
                'مرکز طول جغرافیایی': centroid_lon,
                'مرکز عرض جغرافیایی': centroid_lat,
            })
        df = pd.DataFrame(farm_records)
        df['مساحت (هکتار)'] = pd.to_numeric(df['مساحت (هکتار)'], errors='coerce')
        return df
    except Exception as e:
        st.error(f"خطا در بارگذاری داده‌های مزارع از GEE: {e}")
        return pd.DataFrame()


# --- توابع واکشی داده از GEE ---
@st.cache_data(show_spinner="در حال واکشی داده‌های تبخیر و تعرق از GEE...")
def get_et_data(_farm_geometry, start_date_str, end_date_str):
    if not st.session_state.get('ee_initialized', False) or not _farm_geometry:
        return None, None, None
    try:
        et_collection = ee.ImageCollection('MODIS/061/MOD16A2GF').select('ET', 'PET')
        filtered_et = et_collection.filterDate(start_date_str, end_date_str)
        
        # بررسی اندازه کالکشن ET قبل از .mean() و .clip()
        if filtered_et.size().getInfo() == 0:
            st.warning(f"داده تبخیر و تعرق (ET/PET) برای بازه {start_date_str} تا {end_date_str} یافت نشد.")
            return None, None, None

        mean_et_image_clipped = filtered_et.mean().clip(_farm_geometry)

        stats = mean_et_image_clipped.reduceRegion(
            reducer=ee.Reducer.mean(),
            geometry=_farm_geometry,
            scale=500,
            maxPixels=1e9
        ).getInfo()

        et_kg_m2_8day = stats.get('ET')
        pet_kg_m2_8day = stats.get('PET')

        farm_actual_et_mm_day = (et_kg_m2_8day / 8) if et_kg_m2_8day is not None else None
        reference_et0_mm_day = (pet_kg_m2_8day / 8) if pet_kg_m2_8day is not None else None

        # اطمینان از وجود باندها قبل از انتخاب
        if not mean_et_image_clipped.bandNames().getInfo(): # اگر هیچ باندی وجود نداشته باشد
             return farm_actual_et_mm_day, reference_et0_mm_day, None

        return farm_actual_et_mm_day, reference_et0_mm_day, mean_et_image_clipped.select('ET')
    except ee.EEException as e: # خطاهای خاص GEE
        st.error(f"خطای Google Earth Engine در واکشی داده‌های ET: {e}")
        return None, None, None
    except Exception as e: # سایر خطاها
        st.error(f"خطای عمومی در واکشی داده‌های ET: {e}")
        return None, None, None

# --- تابع واکشی رطوبت خاک (اصلاح شده) ---
@st.cache_data(show_spinner="در حال واکشی داده‌های رطوبت خاک از GEE...")
def get_soil_moisture_data(_farm_geometry, date_str):
    if not st.session_state.get('ee_initialized', False) or not _farm_geometry:
        st.warning("GEE مقداردهی اولیه نشده یا هندسه مزرعه نامعتبر است (برای رطوبت خاک).")
        return None
    try:
        sm_collection = ee.ImageCollection('NASA_USDA/HSL/SMAP_soil_moisture').select(['ssm', 'susm'])
        ee_date = ee.Date(date_str)
        
        filtered_collection = sm_collection.filterDate(ee_date.advance(-2, 'day'), ee_date.advance(2, 'day'))
        
        # --- بررسی کلیدی: ابتدا اندازه کالکشن فیلتر شده را بررسی کنید ---
        collection_size = filtered_collection.size().getInfo()
        
        if collection_size == 0:
            st.warning(f"داده رطوبت خاک برای تاریخ نزدیک به {date_str} یافت نشد (هیچ تصویری در بازه زمانی موجود نیست).")
            return None

        # اگر کالکشن خالی نیست، اولین (جدیدترین) تصویر را بگیرید
        image = filtered_collection.sort('system:time_start', False).first()
        
        # در این مرحله، 'image' یک شیء ee.Image است.
        # بررسی اینکه آیا تصویر واقعاً باند داده‌ای دارد (برای اطمینان بیشتر).
        band_names = image.bandNames().getInfo() # .getInfo() برای اجرای سمت سرور و دریافت نتیجه

        if not band_names: # اگر لیست نام باندها خالی باشد
            st.warning(f"تصویر رطوبت خاک برای تاریخ {date_str} یافت شد اما فاقد باند داده است.")
            return None
            
        return image.clip(_farm_geometry)

    except ee.EEException as e:
        st.error(f"خطای Google Earth Engine در هنگام واکشی داده‌های رطوبت خاک: {e}")
        if "Parameter 'image' is required and may not be null" in str(e) or "Image.load: Image asset not found" in str(e) :
             st.error("توضیح بیشتر: این خطا نشان می‌دهد که شیء تصویر مورد استفاده GEE نامعتبر یا تهی بوده است، یا داده‌ای برای تاریخ مورد نظر یافت نشده است.")
        return None
    except Exception as e:
        st.error(f"خطای عمومی (غیر GEE) در واکشی داده‌های رطوبت خاک: {e}")
        return None

# --- محاسبه نیاز آبی (بدون تغییر) ---
def calculate_water_needs(et0_mm_day, kc, area_ha, irrigation_efficiency=0.7):
    if et0_mm_day is None or kc is None or area_ha is None or pd.isna(area_ha):
        return None, None, None

    etc_mm_day = kc * et0_mm_day
    nir_mm_day = etc_mm_day
    gir_mm_day = nir_mm_day / irrigation_efficiency if irrigation_efficiency > 0 else nir_mm_day
    daily_volume_m3 = gir_mm_day * area_ha * 10
    return etc_mm_day, gir_mm_day, daily_volume_m3

# --- برنامه زمان‌بندی آبیاری (بدون تغییر) ---
def get_irrigation_parameters(farm_info, et0_mm_day_for_kc_estimation):
    age_months_str = farm_info.get('سن')
    try:
        age_months = float(age_months_str) if age_months_str else 0
    except ValueError:
        age_months = 0

    kc_sugarcane = 0.4
    if 0 <= age_months <= 1: kc_sugarcane = 0.35
    elif 1 < age_months <= 3: kc_sugarcane = 0.5
    elif 3 < age_months <= 5: kc_sugarcane = 0.85
    elif 5 < age_months <= 9: kc_sugarcane = 1.20
    elif 9 < age_months <= 11: kc_sugarcane = 1.00
    elif 11 < age_months <= 13: kc_sugarcane = 0.75
    return kc_sugarcane

def get_irrigation_schedule_text(etc_mm_day, soil_water_holding_capacity_mm=150, mad_percentage=0.5, irrigation_efficiency=0.7):
    if etc_mm_day is None or etc_mm_day <= 0:
        return "نیاز به اطلاعات $ET_c$ معتبر (بیشتر از صفر) برای برنامه‌ریزی.", None, None

    readily_available_water_mm = soil_water_holding_capacity_mm * mad_percentage
    irrigation_interval_days = readily_available_water_mm / etc_mm_day
    irrigation_interval_days = round(max(1, irrigation_interval_days))

    net_water_per_irrigation_mm = etc_mm_day * irrigation_interval_days
    gross_water_per_irrigation_mm = net_water_per_irrigation_mm / irrigation_efficiency if irrigation_efficiency > 0 else net_water_per_irrigation_mm

    schedule_info = (
        f"با توجه به $ET_c = {etc_mm_day:.2f}$ mm/day:\n"
        f"- دور آبیاری پیشنهادی: هر **{irrigation_interval_days}** روز.\n"
        f"- مقدار آب خالص مورد نیاز در هر نوبت: **{net_water_per_irrigation_mm:.2f}** میلی‌متر.\n"
        f"- مقدار آب کل مورد نیاز در هر نوبت (با راندمان {irrigation_efficiency*100:.0f}%): **{gross_water_per_irrigation_mm:.2f}** میلی‌متر."
    )
    return schedule_info, irrigation_interval_days, gross_water_per_irrigation_mm


# --- تابع نمایش نقشه (بدون تغییر قابل توجه، اما بررسی vis_params مهم است) ---
def create_ee_map(ee_image_object, center_lat, center_lon, farm_name, zoom_level=12, vis_params=None):
    if not ee_image_object:
        return None
    if center_lat is None or center_lon is None:
        st.warning(f"مختصات مرکز برای مزرعه {farm_name} موجود نیست. نقشه نمایش داده نمی‌شود.")
        return None

    m = folium.Map(location=[center_lat, center_lon], zoom_start=zoom_level, tiles="OpenStreetMap")
    
    # اطمینان از اینکه ee_image_object دارای باند است قبل از تلاش برای تعیین vis_params خودکار
    if not ee_image_object.bandNames().getInfo():
        st.warning(f"تصویر GEE برای {farm_name} فاقد باند است و قابل نمایش روی نقشه نیست.")
        return None # یا یک نقشه خالی برگردانید

    # استفاده از vis_params پیش‌فرض اگر چیزی ارائه نشده باشد
    if vis_params is None:
        vis_params = {'min': 0, 'max': 1000, 'palette': ['blue', 'yellow', 'red']} # پالت بسیار عمومی
        try:
            first_band = ee_image_object.bandNames().getInfo()[0]
            geometry_for_stats = ee_image_object.geometry()
            if geometry_for_stats.coordinates().size().getInfo() > 0 :
                min_val_info = ee_image_object.select(first_band).reduceRegion(
                    reducer=ee.Reducer.min(), geometry=geometry_for_stats, scale=30, maxPixels=1e9
                ).get(first_band).getInfo()
                max_val_info = ee_image_object.select(first_band).reduceRegion(
                    reducer=ee.Reducer.max(), geometry=geometry_for_stats, scale=30, maxPixels=1e9
                ).get(first_band).getInfo()
                if min_val_info is not None and max_val_info is not None and min_val_info < max_val_info:
                    vis_params = {'min': min_val_info, 'max': max_val_info, 'palette': ['#fde725', '#5ec962', '#21918c', '#3b528b', '#440154']}
        except Exception:
             pass # اگر تعیین خودکار ناموفق بود، از پیش‌فرض استفاده می‌شود

    try:
        tile_url = ee_image_object.getMapId(vis_params)['tile_fetcher'].url_format
        folium.TileLayer(
            tiles=tile_url,
            attr=f'Google Earth Engine - {farm_name}',
            name='EE Image Layer',
            overlay=True,
            control=True
        ).add_to(m)
        folium.LayerControl().add_to(m)
    except Exception as e:
        st.error(f"خطا در افزودن لایه GEE به نقشه برای {farm_name}: {e}")
        return None
    return m

# --- Streamlit App (بخش UI بدون تغییرات عمده نسبت به قبل) ---
st.set_page_config(layout="wide", page_title="محاسبه و برنامه‌ریزی آبیاری مزارع")

st.title("سامانه هوشمند مدیریت آبیاری مزارع")
st.subheader("ویژه مزارع نیشکر شرکت دهخدا، خوزستان")
st.markdown("---")

if initialize_gee():
    farm_data_df = load_farm_data_from_gee()

    if not farm_data_df.empty:
        st.sidebar.header("انتخاب مزرعه")
        farm_names = sorted(farm_data_df['مزرعه'].unique().tolist())
        selected_farm_name = st.sidebar.selectbox("انتخاب مزرعه:", farm_names)

        selected_farm_info = farm_data_df[farm_data_df['مزرعه'] == selected_farm_name].iloc[0]
        farm_ee_geometry = selected_farm_info['ee_geometry']
        farm_area_ha = selected_farm_info['مساحت (هکتار)']
        farm_variety = selected_farm_info['واریته']
        farm_age = selected_farm_info['سن']
        farm_centroid_lat = selected_farm_info['مرکز عرض جغرافیایی']
        farm_centroid_lon = selected_farm_info['مرکز طول جغرافیایی']

        st.header(f"اطلاعات و محاسبات برای مزرعه: {selected_farm_name}")
        col_info1, col_info2, col_info3 = st.columns(3)
        with col_info1: st.metric("واریته", farm_variety if farm_variety else "نامشخص")
        with col_info2: st.metric("سن تقریبی (ماه)", str(farm_age) if farm_age else "نامشخص")
        with col_info3: st.metric("مساحت (هکتار)", f"{farm_area_ha:.2f}" if pd.notna(farm_area_ha) else "نامشخص")

        st.markdown("#### تنظیم پارامترهای ورودی")
        col_param1, col_param2, col_param3 = st.columns(3)
        with col_param1:
            default_et_end_date = datetime.today() - timedelta(days=1)
            default_et_start_date = default_et_end_date - timedelta(days=7)
            start_date = st.date_input("تاریخ شروع دوره ET:", default_et_start_date, key="et_start")
            end_date = st.date_input("تاریخ پایان دوره ET:", default_et_end_date, key="et_end")
        with col_param2:
            sm_date = st.date_input("تاریخ برای رطوبت خاک:", datetime.today() - timedelta(days=1), key="sm_date")
            irrigation_efficiency = st.slider("راندمان آبیاری کل مزرعه:", 0.1, 1.0, 0.7, 0.05, key="irrig_eff")
        with col_param3:
            suggested_kc = get_irrigation_parameters(selected_farm_info, None)
            kc_value = st.number_input(
                f"ضریب گیاهی ($K_c$) (پیشنهادی: {suggested_kc:.2f}):",
                min_value=0.1, max_value=2.5, value=suggested_kc, step=0.05, key="kc_val",
                help=f"بر اساس سن تقریبی {farm_age} ماه و واریته {farm_variety}. در صورت نیاز اصلاح کنید."
            )

        if start_date > end_date:
            st.error("تاریخ شروع دوره ET نمی‌تواند بعد از تاریخ پایان آن باشد.")
        else:
            start_date_str = start_date.strftime('%Y-%m-%d')
            end_date_str = end_date.strftime('%Y-%m-%d')
            sm_date_str = sm_date.strftime('%Y-%m-%d')

            st.markdown("---")
            st.subheader(f"نتایج محاسبات نیاز آبی (دوره {start_date_str} تا {end_date_str})")

            if farm_ee_geometry and pd.notna(farm_area_ha):
                farm_actual_et_mm_day, reference_et0_mm_day, et_image_for_map = get_et_data(farm_ee_geometry, start_date_str, end_date_str)

                col_metric1, col_metric2, col_metric3 = st.columns(3)
                if farm_actual_et_mm_day is not None:
                    col_metric1.metric("ET واقعی مزرعه (MODIS)", f"{farm_actual_et_mm_day:.2f} mm/day", help="میانگین تبخیر و تعرق واقعی برآورد شده از تصاویر MODIS برای این مزرعه در دوره مشخص شده.")
                else: col_metric1.warning("ET واقعی مزرعه در دسترس نیست.")

                if reference_et0_mm_day is not None:
                    col_metric2.metric(" $ET_0$ مرجع (MODIS PET)", f"{reference_et0_mm_day:.2f} mm/day", help="میانگین تبخیر و تعرق مرجع (پتانسیل) برآورد شده از تصاویر MODIS PET برای این مزرعه در دوره مشخص شده.")
                    etc_mm_day, gir_mm_day, daily_volume_m3 = calculate_water_needs(reference_et0_mm_day, kc_value, farm_area_ha, irrigation_efficiency)
                    if etc_mm_day is not None:
                        col_metric3.metric("$ET_c$ محاسبه شده گیاه", f"{etc_mm_day:.2f} mm/day", help=f"$K_c \\times ET_0 = {kc_value:.2f} \\times {reference_et0_mm_day:.2f}$")
                        st.success(f"**نیاز آبی گیاه ($ET_c$): {etc_mm_day:.2f} mm/day** | **نیاز آبی کل ($GIR$): {gir_mm_day:.2f} mm/day** | **حجم آب روزانه مورد نیاز: {daily_volume_m3:.1f} متر مکعب**")
                        st.markdown("#### برنامه زمان‌بندی آبیاری (پیشنهادی)")
                        col_sched1, col_sched2 = st.columns(2)
                        with col_sched1: soil_capacity = st.number_input("ظرفیت نگهداری آب خاک در منطقه ریشه (mm):", value=120, min_value=20, max_value=300, step=10, key="soil_cap")
                        with col_sched2: mad_percent = st.slider("درصد تخلیه مجاز رطوبت (MAD):", 0.1, 0.8, 0.5, 0.05, key="mad", help="مثلاً 0.5 یعنی 50% تخلیه مجاز است.")
                        schedule_text, _, _ = get_irrigation_schedule_text(etc_mm_day, soil_capacity, mad_percent, irrigation_efficiency)
                        st.info(schedule_text)
                    else: st.warning("امکان محاسبه $ET_c$ و نیاز آبی وجود ندارد. مقادیر $ET_0$ یا $K_c$ معتبر نیستند.")
                else:
                    col_metric2.warning("$ET_0$ مرجع در دسترس نیست.")
                    st.warning(f"داده‌های تبخیر و تعرق مرجع ($ET_0$) برای دوره انتخاب شده یافت نشد یا خطایی رخ داده است.")

                st.markdown("---")
                st.subheader("نقشه‌های ماهواره‌ای")
                tab1, tab2 = st.tabs(["نقشه ET واقعی مزرعه", "نقشه رطوبت خاک"])

                with tab1:
                    st.markdown(f"**نمایش میانگین تبخیر و تعرق واقعی (ET) برای مزرعه {selected_farm_name} ({start_date_str} تا {end_date_str})**")
                    if et_image_for_map and farm_centroid_lat and farm_centroid_lon:
                        et_vis_params = {'min': 0, 'max': 10, 'palette': ['#ffffcc', '#c7e9b4', '#7fcdbb', '#41b6c4', '#2c7fb8', '#253494']}
                        et_map = create_ee_map(et_image_for_map, farm_centroid_lat, farm_centroid_lon, selected_farm_name, zoom_level=15, vis_params=et_vis_params)
                        if et_map: st_folium(et_map, width=None, height=500, returned_objects=[])
                        else: st.warning("نقشه تبخیر و تعرق واقعی قابل نمایش نیست.")
                    else: st.info("داده‌های ET واقعی برای نمایش روی نقشه آماده نیست (ممکن است تصویری در بازه زمانی یافت نشده باشد) یا مرکز مزرعه مشخص نیست.")

                with tab2:
                    st.markdown(f"**نمایش رطوبت سطحی خاک (SSM) برای مزرعه {selected_farm_name} (نزدیک به تاریخ {sm_date_str})**")
                    soil_moisture_image = get_soil_moisture_data(farm_ee_geometry, sm_date_str) # فراخوانی تابع اصلاح شده
                    if soil_moisture_image and farm_centroid_lat and farm_centroid_lon:
                        sm_vis_params = {'bands': ['ssm'], 'min': 0.0, 'max': 40.0, 'palette': ['#feebe2','#fbb4b9','#f768a1','#c51b8a','#7a0177']}
                        sm_map = create_ee_map(soil_moisture_image, farm_centroid_lat, farm_centroid_lon, selected_farm_name, zoom_level=15, vis_params=sm_vis_params)
                        if sm_map: st_folium(sm_map, width=None, height=500, returned_objects=[])
                        else: st.warning("نقشه رطوبت خاک قابل نمایش نیست.")
                    else: st.info(f"داده‌های رطوبت خاک برای تاریخ {sm_date_str} یافت نشد یا برای نمایش روی نقشه آماده نیست یا مرکز مزرعه مشخص نیست.")
            else:
                st.error(f"اطلاعات هندسی یا مساحت برای مزرعه {selected_farm_name} یافت نشد. امکان انجام محاسبات و نمایش نقشه وجود ندارد.")
    elif st.session_state.get('ee_initialized', False): # اگر GEE وصل است ولی داده مزرعه خالی است
        st.error("داده‌های مزارع بارگذاری نشدند یا خالی هستند. لطفاً FeatureCollection خود را در GEE و مسیر فایل کلید بررسی کنید.")
    else: # اگر GEE مقداردهی اولیه نشده است
        st.warning("اتصال به Google Earth Engine برقرار نشد. لطفاً ابتدا از صحت مسیر فایل Service Account اطمینان حاصل کرده و منتظر اتصال بمانید یا صفحه را مجدداً بارگذاری کنید.")

st.sidebar.markdown("---")
st.sidebar.info("توسعه داده شده با Streamlit و Google Earth Engine")
st.sidebar.info(f"تاریخ اجرا: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")