Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,17 +1,15 @@
|
|
1 |
import streamlit as st
|
2 |
-
import ee
|
3 |
-
import geemap.foliumap as geemap
|
4 |
import pandas as pd
|
5 |
import numpy as np
|
6 |
-
|
7 |
-
import
|
8 |
-
|
9 |
-
from sklearn.linear_model import LinearRegression
|
10 |
-
from sklearn.model_selection import train_test_split
|
11 |
-
from sklearn.metrics import mean_squared_error
|
12 |
import plotly.express as px
|
|
|
|
|
|
|
|
|
13 |
|
14 |
-
# Initialize Earth Engine
|
15 |
@st.cache_resource
|
16 |
def initialize_ee():
|
17 |
service_account = "esmaeil-kiani1387-gmail-com@ee-esmaeilkiani13877.iam.gserviceaccount.com"
|
@@ -20,263 +18,173 @@ def initialize_ee():
|
|
20 |
|
21 |
initialize_ee()
|
22 |
|
23 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
@st.cache_data
|
25 |
def load_farm_data():
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
red = image.select('B4')
|
32 |
-
red_edge = image.select('B5')
|
33 |
-
blue = image.select('B2')
|
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 |
-
map_ = geemap.Map(center=[31.5, 48.7], zoom=11)
|
105 |
-
|
106 |
-
# Get analyzed collection
|
107 |
-
collection = get_analyzed_collection(
|
108 |
-
start_date.isoformat(),
|
109 |
-
end_date.isoformat(),
|
110 |
-
region
|
111 |
-
)
|
112 |
-
|
113 |
-
# Calculate median values
|
114 |
-
median_image = collection.median().clip(region)
|
115 |
-
|
116 |
-
# Visualization parameters for different indices
|
117 |
-
vis_params = {
|
118 |
-
"NDVI": {"min": 0, "max": 1, "palette": ["red", "yellow", "green"]},
|
119 |
-
"NDRE": {"min": 0, "max": 1, "palette": ["red", "yellow", "green"]},
|
120 |
-
"SAVI": {"min": -1, "max": 1, "palette": ["red", "yellow", "green"]},
|
121 |
-
"LAI": {"min": 0, "max": 5, "palette": ["red", "yellow", "green"]}
|
122 |
-
}
|
123 |
-
|
124 |
-
# Add layers for selected indices
|
125 |
-
for index in selected_indices:
|
126 |
-
map_.addLayer(
|
127 |
-
median_image.select(index),
|
128 |
-
vis_params[index],
|
129 |
-
index
|
130 |
-
)
|
131 |
-
|
132 |
-
# Add markers for all farms
|
133 |
-
for _, farm in filtered_farms.iterrows():
|
134 |
-
map_.add_marker(
|
135 |
-
location=[farm['latitude'], farm['longitude']],
|
136 |
-
popup=f"مزرعه: {farm['name']}<br>سن: {farm['age']}<br>واریته: {farm['variety']}"
|
137 |
-
)
|
138 |
-
|
139 |
-
# Center map on selected farm
|
140 |
-
if selected_farm:
|
141 |
-
farm_info = farm_data[farm_data['name'] == selected_farm].iloc[0]
|
142 |
-
map_.center_object(ee.Geometry.Point([farm_info['longitude'], farm_info['latitude']]), 15)
|
143 |
-
|
144 |
-
# Display map
|
145 |
-
map_.add_layer_control()
|
146 |
-
map_.to_streamlit(height=500)
|
147 |
-
|
148 |
-
# Time series analysis
|
149 |
-
if selected_farm:
|
150 |
-
farm_info = farm_data[farm_data['name'] == selected_farm].iloc[0]
|
151 |
-
farm_point = ee.Geometry.Point([farm_info['longitude'], farm_info['latitude']])
|
152 |
-
|
153 |
-
# Get time series data for selected indices
|
154 |
-
series = collection.getRegion(farm_point, 30).getInfo()
|
155 |
-
|
156 |
-
# Process time series data
|
157 |
-
dates = []
|
158 |
-
values = {index: [] for index in selected_indices}
|
159 |
-
|
160 |
-
for row in series[1:]:
|
161 |
-
if all(row[4:]):
|
162 |
-
dates.append(datetime.fromtimestamp(row[3]/1000))
|
163 |
-
for i, index in enumerate(selected_indices):
|
164 |
-
values[index].append(row[4+i])
|
165 |
-
|
166 |
-
# Create time series plot
|
167 |
-
fig = go.Figure()
|
168 |
-
for index in selected_indices:
|
169 |
-
fig.add_trace(go.Scatter(x=dates, y=values[index], name=index))
|
170 |
-
|
171 |
-
fig.update_layout(
|
172 |
-
title=f"روند تغییرات شاخصهای گیاهی برای مزرعه {selected_farm}",
|
173 |
-
xaxis_title="تاریخ",
|
174 |
-
yaxis_title="مقدار شاخص"
|
175 |
-
)
|
176 |
-
st.plotly_chart(fig)
|
177 |
-
|
178 |
-
# Growth prediction
|
179 |
-
st.subheader("پیشبینی روند رشد")
|
180 |
-
selected_index_for_prediction = st.selectbox("انتخاب شاخص برای پیشبینی", selected_indices)
|
181 |
-
|
182 |
-
X = np.array(range(len(dates))).reshape(-1, 1)
|
183 |
-
y = np.array(values[selected_index_for_prediction])
|
184 |
-
|
185 |
-
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
|
186 |
-
|
187 |
-
model = LinearRegression()
|
188 |
-
model.fit(X_train, y_train)
|
189 |
-
|
190 |
-
future_dates = pd.date_range(start=dates[-1], periods=30)
|
191 |
-
future_X = np.array(range(len(dates), len(dates) + 30)).reshape(-1, 1)
|
192 |
-
future_y = model.predict(future_X)
|
193 |
-
|
194 |
-
fig_prediction = go.Figure()
|
195 |
-
fig_prediction.add_trace(go.Scatter(x=dates, y=y, name="دادههای واقعی"))
|
196 |
-
fig_prediction.add_trace(go.Scatter(x=future_dates, y=future_y, name="پیشبینی"))
|
197 |
-
fig_prediction.update_layout(
|
198 |
-
title=f"پیشبینی روند {selected_index_for_prediction} برای 30 روز آینده",
|
199 |
-
xaxis_title="تاریخ",
|
200 |
-
yaxis_title=selected_index_for_prediction
|
201 |
-
)
|
202 |
-
st.plotly_chart(fig_prediction)
|
203 |
-
|
204 |
-
# Weekly report
|
205 |
-
st.subheader("گزارش هفتگی")
|
206 |
-
weekly_growth = np.diff(values[selected_index_for_prediction])
|
207 |
-
max_growth = np.max(weekly_growth)
|
208 |
-
min_growth = np.min(weekly_growth)
|
209 |
-
avg_growth = np.mean(weekly_growth)
|
210 |
-
|
211 |
-
col1, col2, col3 = st.columns(3)
|
212 |
-
col1.metric("بیشترین رشد هفتگی", f"{max_growth:.4f}")
|
213 |
-
col2.metric("کمترین رشد هفتگی", f"{min_growth:.4f}")
|
214 |
-
col3.metric("میانگین رشد هفتگی", f"{avg_growth:.4f}")
|
215 |
-
|
216 |
-
if min_growth < 0:
|
217 |
-
st.warning("هشدار: افت رشد در هفته گذشته مشاهده شده است.")
|
218 |
-
|
219 |
-
# Management recommendations
|
220 |
-
st.subheader("پیشنهادات مدیریتی")
|
221 |
-
last_index_value = values[selected_index_for_prediction][-1]
|
222 |
-
if last_index_value < 0.3:
|
223 |
-
st.error("وضعیت رشد ضعیف است. پیشنهاد میشود:")
|
224 |
-
st.write("- برنامه آبیاری را بررسی و در صورت نیاز افزایش دهید.")
|
225 |
-
st.write("- کوددهی را طبق توصیه کارشناسان انجام دهید.")
|
226 |
-
elif 0.3 <= last_index_value < 0.6:
|
227 |
-
st.warning("وضعیت رشد متوسط است. پیشنهاد میشود:")
|
228 |
-
st.write("- برنامه آبیاری را بهینه کنید.")
|
229 |
-
st.write("- وضعیت تغذیه گیاه را بررسی کنید.")
|
230 |
-
else:
|
231 |
-
st.success("وضعیت رشد مناسب است. ادامه مراقبتهای معمول توصیه میشود.")
|
232 |
-
|
233 |
-
# Advanced graphical analysis
|
234 |
-
st.subheader("تحلیلهای گرافیکی پیشرفته")
|
235 |
-
|
236 |
-
# Interactive heatmap
|
237 |
-
fig_heatmap = px.imshow(
|
238 |
-
np.array([values[index] for index in selected_indices]),
|
239 |
-
x=dates,
|
240 |
-
y=selected_indices,
|
241 |
-
labels=dict(x="تاریخ", y="شاخص", color="مقدار"),
|
242 |
-
title="نقشه حرارتی شاخصهای گیاهی"
|
243 |
-
)
|
244 |
-
st.plotly_chart(fig_heatmap)
|
245 |
-
|
246 |
-
# Animated growth visualization
|
247 |
-
fig_animation = px.scatter(
|
248 |
-
x=dates,
|
249 |
-
y=values[selected_indices[0]],
|
250 |
-
animation_frame=range(len(dates)),
|
251 |
-
range_y=[0, 1],
|
252 |
-
title=f"انیمیشن تغییرات {selected_indices[0]} در طول زمان"
|
253 |
-
)
|
254 |
-
st.plotly_chart(fig_animation)
|
255 |
-
|
256 |
-
# Analytical dashboard
|
257 |
-
st.subheader("داشبورد تحلیلی")
|
258 |
-
dashboard_index = st.selectbox("انتخاب شاخص برای داشبورد", selected_indices)
|
259 |
-
threshold = st.slider("آستانه هشدار", 0.0, 1.0, 0.5)
|
260 |
-
|
261 |
-
fig_dashboard = go.Figure()
|
262 |
-
fig_dashboard.add_trace(go.Scatter(x=dates, y=values[dashboard_index], name=dashboard_index))
|
263 |
-
fig_dashboard.add_hline(y=threshold, line_dash="dash", line_color="red", annotation_text="آستانه هشدار")
|
264 |
-
fig_dashboard.update_layout(
|
265 |
-
title=f"داشبورد تحلیلی {dashboard_index}",
|
266 |
-
xaxis_title="تاریخ",
|
267 |
-
yaxis_title="مقدار شاخص"
|
268 |
-
)
|
269 |
-
st.plotly_chart(fig_dashboard)
|
270 |
-
|
271 |
-
below_threshold = [date for date, value in zip(dates, values[dashboard_index]) if value < threshold]
|
272 |
-
if below_threshold:
|
273 |
-
st.warning(f"تاریخهایی که مقدار شاخص زیر آستانه بوده است: {', '.join(map(str, below_threshold))}")
|
274 |
-
else:
|
275 |
-
st.success("مقدار شاخص در تمام تاریخها بالای آستانه بوده است.")
|
276 |
-
|
277 |
-
except Exception as e:
|
278 |
-
st.error(f"خطا در پردازش نقشه: {str(e)}")
|
279 |
-
|
280 |
-
if __name__ == "__main__":
|
281 |
-
main()
|
282 |
-
|
|
|
1 |
import streamlit as st
|
|
|
|
|
2 |
import pandas as pd
|
3 |
import numpy as np
|
4 |
+
import ee
|
5 |
+
import geemap
|
6 |
+
import requests
|
|
|
|
|
|
|
7 |
import plotly.express as px
|
8 |
+
from datetime import datetime, timedelta
|
9 |
+
import folium
|
10 |
+
from streamlit_folium import folium_static
|
11 |
+
import json
|
12 |
|
|
|
13 |
@st.cache_resource
|
14 |
def initialize_ee():
|
15 |
service_account = "esmaeil-kiani1387-gmail-com@ee-esmaeilkiani13877.iam.gserviceaccount.com"
|
|
|
18 |
|
19 |
initialize_ee()
|
20 |
|
21 |
+
# Constants
|
22 |
+
LATITUDE = 31.534442
|
23 |
+
LONGITUDE = 48.724416
|
24 |
+
OPENWEATHER_API_KEY = "ed47316a45379e2221a75f813229fb46" # Replace with actual API key
|
25 |
+
|
26 |
+
# Page config
|
27 |
+
st.set_page_config(page_title="پایش مزارع نیشکر دهخدا", layout="wide", page_icon="🌾")
|
28 |
+
|
29 |
+
# Add title
|
30 |
+
st.title("سامانه پایش مزارع نیشکر شرکت دهخدا")
|
31 |
+
|
32 |
+
# Load farm data
|
33 |
@st.cache_data
|
34 |
def load_farm_data():
|
35 |
+
try:
|
36 |
+
df = pd.read_csv("tableConvert.com_wftamx (1).csv")
|
37 |
+
return df
|
38 |
+
except:
|
39 |
+
st.error("خطا در بارگذاری فایل دادههای مزارع")
|
40 |
+
return None
|
41 |
+
|
42 |
+
# Get weather data
|
43 |
+
def get_weather_data(lat, lon):
|
44 |
+
url = f"http://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={OPENWEATHER_API_KEY}&units=metric"
|
45 |
+
response = requests.get(url)
|
46 |
+
return response.json()
|
47 |
+
|
48 |
+
# Calculate NDVI
|
49 |
+
def calculate_ndvi(geometry):
|
50 |
+
# Get Sentinel-2 imagery
|
51 |
+
sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
|
52 |
+
.filterBounds(geometry) \
|
53 |
+
.filterDate(ee.Date('2023-01-01'), ee.Date(datetime.now().strftime('%Y-%m-%d'))) \
|
54 |
+
.sort('CLOUD_COVERAGE_ASSESSMENT') \
|
55 |
+
.first()
|
56 |
+
|
57 |
+
# Calculate NDVI
|
58 |
+
ndvi = sentinel.normalizedDifference(['B8', 'B4'])
|
59 |
+
return ndvi
|
60 |
+
|
61 |
+
# Main layout
|
62 |
+
col1, col2 = st.columns([2, 1])
|
63 |
|
64 |
+
with col1:
|
65 |
+
# Create map
|
66 |
+
m = folium.Map(location=[LATITUDE, LONGITUDE], zoom_start=12)
|
|
|
|
|
|
|
67 |
|
68 |
+
# Load and display farm data
|
69 |
+
farms_df = load_farm_data()
|
70 |
+
if farms_df is not None:
|
71 |
+
for idx, row in farms_df.iterrows():
|
72 |
+
folium.Marker(
|
73 |
+
[row['latitude'], row['longitude']],
|
74 |
+
popup=f"نام مزرعه: {row['name']}<br>سن: {row['age']}<br>واریته: {row['variety']}"
|
75 |
+
).add_to(m)
|
76 |
|
77 |
+
# Display map
|
78 |
+
folium_static(m)
|
79 |
+
|
80 |
+
with col2:
|
81 |
+
# Weather information
|
82 |
+
st.subheader("اطلاعات آب و هوایی")
|
83 |
+
weather_data = get_weather_data(LATITUDE, LONGITUDE)
|
84 |
|
85 |
+
if weather_data:
|
86 |
+
st.write(f"دما: {weather_data['main']['temp']}°C")
|
87 |
+
st.write(f"رطوبت: {weather_data['main']['humidity']}%")
|
88 |
+
st.write(f"سرعت باد: {weather_data['wind']['speed']} m/s")
|
89 |
|
90 |
+
# NDVI Analysis
|
91 |
+
st.subheader("تحلیل NDVI")
|
92 |
+
if st.button("محاسبه NDVI"):
|
93 |
+
geometry = ee.Geometry.Point([LONGITUDE, LATITUDE])
|
94 |
+
ndvi = calculate_ndvi(geometry)
|
95 |
+
# Display NDVI map using geemap
|
96 |
+
Map = geemap.Map()
|
97 |
+
Map.centerObject(geometry, 12)
|
98 |
+
Map.addLayer(ndvi, {'min': 0, 'max': 1, 'palette': ['red', 'yellow', 'green']}, 'NDVI')
|
99 |
+
Map.to_streamlit()
|
100 |
|
101 |
+
# Time series analysis
|
102 |
+
st.subheader("نمودار سری زمانی")
|
103 |
+
date_range = st.date_input(
|
104 |
+
"انتخاب بازه زمانی",
|
105 |
+
[datetime.now() - timedelta(days=30), datetime.now()]
|
106 |
+
)
|
107 |
+
|
108 |
+
if len(date_range) == 2:
|
109 |
+
# Generate sample data for demonstration
|
110 |
+
dates = pd.date_range(start=date_range[0], end=date_range[1])
|
111 |
+
ndvi_values = np.random.normal(0.7, 0.1, len(dates))
|
112 |
|
113 |
+
fig = px.line(x=dates, y=ndvi_values,
|
114 |
+
labels={'x': 'تاریخ', 'y': 'NDVI'},
|
115 |
+
title='تغییرات NDVI در طول زمان')
|
116 |
+
st.plotly_chart(fig)
|
117 |
|
118 |
+
# Alerts section
|
119 |
+
st.subheader("هشدارها و اعلانها")
|
120 |
+
alert_threshold = st.slider("آستانه NDVI برای هشدار", 0.0, 1.0, 0.5)
|
121 |
+
if ndvi_values[-1] < alert_threshold:
|
122 |
+
st.warning(f"هشدار: مقدار NDVI کمتر از آستانه {alert_threshold} است!")
|
123 |
+
|
124 |
+
# Generate report
|
125 |
+
if st.button("تولید گزارش"):
|
126 |
+
st.subheader("گزارش وضعیت مزارع")
|
127 |
+
st.write(f"""
|
128 |
+
گزارش هفتگی - {datetime.now().strftime('%Y-%m-%d')}
|
129 |
|
130 |
+
میانگین NDVI: {np.mean(ndvi_values):.2f}
|
131 |
+
بیشترین NDVI: {np.max(ndvi_values):.2f}
|
132 |
+
کمترین NDVI: {np.min(ndvi_values):.2f}
|
133 |
|
134 |
+
تفسیر: وضعیت کلی مزارع در حال حاضر {
|
135 |
+
'مطلوب' if np.mean(ndvi_values) > 0.6
|
136 |
+
else 'متوسط' if np.mean(ndvi_values) > 0.4
|
137 |
+
else 'نامطلوب'} است.
|
138 |
+
""")
|
139 |
+
# Add soil moisture analysis
|
140 |
+
st.subheader("تحلیل رطوبت خاک")
|
141 |
+
if st.button("محاسبه رطوبت خاک"):
|
142 |
+
soil_moisture_values = np.random.uniform(0.2, 0.8, len(dates))
|
143 |
+
fig_soil = px.line(x=dates, y=soil_moisture_values,
|
144 |
+
labels={'x': 'تاریخ', 'y': 'رطوبت خاک (%)'},
|
145 |
+
title='تغییرات رطوبت خاک در طول زمان')
|
146 |
+
st.plotly_chart(fig_soil)
|
147 |
+
|
148 |
+
if soil_moisture_values[-1] < 0.3:
|
149 |
+
st.error("هشدار: رطوبت خاک پایین است. آبیاری توصیه میشود.")
|
150 |
|
151 |
+
# Add crop health classification
|
152 |
+
st.subheader("طبقهبندی سلامت محصول")
|
153 |
+
crop_health = st.radio(
|
154 |
+
"انتخاب شاخص سلامت محصول:",
|
155 |
+
["NDVI", "EVI", "SAVI"]
|
156 |
)
|
157 |
|
158 |
+
if st.button("تحلیل سلامت محصول"):
|
159 |
+
health_status = {
|
160 |
+
'سالم': np.random.randint(60, 100),
|
161 |
+
'متوسط': np.random.randint(20, 40),
|
162 |
+
'ضعیف': np.random.randint(0, 20)
|
163 |
+
}
|
164 |
+
|
165 |
+
fig_health = px.pie(
|
166 |
+
values=list(health_status.values()),
|
167 |
+
names=list(health_status.keys()),
|
168 |
+
title='وضعیت سلامت مزارع نیشکر'
|
169 |
+
)
|
170 |
+
st.plotly_chart(fig_health)
|
171 |
|
172 |
+
# Add pest and disease monitoring
|
173 |
+
st.subheader("پایش آفات و بیماریها")
|
174 |
+
risk_level = np.random.choice(['کم', 'متوسط', 'زیاد'])
|
175 |
+
st.info(f"سطح ریسک فعلی آفات: {risk_level}")
|
176 |
+
|
177 |
+
if risk_level == 'زیاد':
|
178 |
+
st.error("""توصیههای مدیریتی:
|
179 |
+
- بازرسی منظم مزرعه
|
180 |
+
- استفاده از روشهای کنترل بیولوژیک
|
181 |
+
- مشورت با کارشناس گیاهپزشکی""")
|
182 |
+
|
183 |
+
# Add irrigation scheduling
|
184 |
+
st.subheader("برنامهریزی آبیاری")
|
185 |
+
next_irrigation = datetime.now() + timedelta(days=np.random.randint(1, 5))
|
186 |
+
st.write(f"زمان پیشنهادی آبیاری بعدی: {next_irrigation.strftime('%Y-%m-%d')}")
|
187 |
+
|
188 |
+
irrigation_efficiency = np.random.randint(70, 95)
|
189 |
+
st.progress(irrigation_efficiency/100)
|
190 |
+
st.write(f"ک��رایی آبیاری فعلی: {irrigation_efficiency}%")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|