JAMESPARK3 commited on
Commit
d44b4f9
ยท
verified ยท
1 Parent(s): aa07440

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +384 -0
app.py ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import matplotlib.pyplot as plt
4
+ import google.generativeai as genai
5
+ from datetime import datetime, timedelta
6
+ import time
7
+ import xml.etree.ElementTree as ET
8
+ import requests
9
+ import matplotlib.font_manager as fm
10
+ import os
11
+
12
+ # ํฐํŠธ ์„ค์ •
13
+ font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'
14
+ font_prop = fm.FontProperties(fname=font_path)
15
+ plt.rcParams['font.family'] = 'NanumGothic'
16
+ plt.rcParams['font.sans-serif'] = ['NanumGothic']
17
+ plt.rcParams['axes.unicode_minus'] = False
18
+ fm.fontManager.addfont(font_path)
19
+
20
+ # API ํ‚ค ์„ค์ •
21
+ GEMINI_API_KEY = os.environ.get('GEMINI_API_KEY')
22
+ WEATHER_API_KEY = os.environ.get('WEATHER_API_KEY')
23
+
24
+ # Gemini ๋ชจ๋ธ ์„ค์ •
25
+ genai.configure(api_key=GEMINI_API_KEY)
26
+ model = genai.GenerativeModel('gemini-2.0-flash-exp')
27
+
28
+ def get_weather_data(base_date):
29
+ url = "https://apihub.kma.go.kr/api/typ02/openApi/VilageFcstInfoService_2.0/getVilageFcst"
30
+ params = {
31
+ 'pageNo': '1',
32
+ 'numOfRows': '1000',
33
+ 'dataType': 'XML',
34
+ 'base_date': base_date,
35
+ 'base_time': '0500',
36
+ 'nx': '59',
37
+ 'ny': '125',
38
+ 'authKey': WEATHER_API_KEY
39
+ }
40
+ response = requests.get(url, params=params)
41
+ response.raise_for_status()
42
+ return response.text
43
+
44
+ def parse_weather_data(xml_data):
45
+ root = ET.fromstring(xml_data)
46
+ all_data = []
47
+
48
+ for item in root.findall('.//item'):
49
+ all_data.append({
50
+ 'base_date': item.find('baseDate').text,
51
+ 'category': item.find('category').text,
52
+ 'fcstTime': item.find('fcstTime').text,
53
+ 'fcstValue': item.find('fcstValue').text,
54
+ 'fcstDate': item.find('fcstDate').text
55
+ })
56
+
57
+ return pd.DataFrame(all_data)
58
+
59
+ def analyze_weather_trends(df):
60
+ current_time = datetime.now()
61
+ tomorrow = (current_time + timedelta(days=1)).strftime("%Y%m%d")
62
+ print(f"ํ˜„์žฌ ์‹œ๊ฐ: {current_time}")
63
+
64
+ # ์˜ค๋Š˜๊ณผ ๋‚ด์ผ ๋‚ ์งœ์˜ ๋ฐ์ดํ„ฐ ๋ถ„๋ฆฌ
65
+ today_data = df[df['fcstDate'] == current_time.strftime("%Y%m%d")]
66
+ tomorrow_data = df[df['fcstDate'] == tomorrow]
67
+
68
+ # ์˜ค๋Š˜ ๊ธฐ์˜จ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
69
+ temp_data = today_data[today_data['category'] == 'TMP'].copy()
70
+ if not temp_data.empty:
71
+ temp_data['fcst_datetime'] = pd.to_datetime(temp_data['fcstDate'] + temp_data['fcstTime'], format='%Y%m%d%H00')
72
+ closest_time_data = temp_data.iloc[(temp_data['fcst_datetime'] - current_time).abs().argsort()[:1]]
73
+ current_temp = float(closest_time_data['fcstValue'].iloc[0])
74
+ else:
75
+ current_temp = 0
76
+
77
+ # ์˜ค๋Š˜ ์‹œ๊ฐ„๋Œ€๋ณ„ ๊ธฐ์˜จ ๊ณ„์‚ฐ
78
+ temp_data['hour'] = temp_data['fcstTime'].str[:2].astype(int)
79
+ morning_temps = temp_data[temp_data['hour'].between(6, 11)]['fcstValue'].astype(float)
80
+ afternoon_temps = temp_data[temp_data['hour'].between(12, 17)]['fcstValue'].astype(float)
81
+ evening_temps = temp_data[temp_data['hour'].between(18, 23)]['fcstValue'].astype(float)
82
+
83
+ # ์˜ค๋Š˜ ํ•˜๋Š˜์ƒํƒœ ๋ฐ์ดํ„ฐ
84
+ sky_data = today_data[today_data['category'] == 'SKY'].copy()
85
+ if not sky_data.empty:
86
+ sky_data['fcst_datetime'] = pd.to_datetime(sky_data['fcstDate'] + sky_data['fcstTime'], format='%Y%m%d%H00')
87
+ morning_sky_data = sky_data[sky_data['fcst_datetime'].dt.hour.between(6, 11)].sort_values(by='fcst_datetime').iloc[0] if len(sky_data[sky_data['fcst_datetime'].dt.hour.between(6, 11)]) > 0 else None
88
+ afternoon_sky_data = sky_data[sky_data['fcst_datetime'].dt.hour.between(12, 17)].sort_values(by='fcst_datetime').iloc[0] if len(sky_data[sky_data['fcst_datetime'].dt.hour.between(12, 17)]) > 0 else None
89
+ evening_sky_data = sky_data[sky_data['fcst_datetime'].dt.hour.between(18, 23)].sort_values(by='fcst_datetime').iloc[0] if len(sky_data[sky_data['fcst_datetime'].dt.hour.between(18, 23)]) > 0 else None
90
+
91
+ morning_sky = morning_sky_data['fcstValue'] if morning_sky_data is not None else '1'
92
+ afternoon_sky = afternoon_sky_data['fcstValue'] if afternoon_sky_data is not None else '1'
93
+ evening_sky = evening_sky_data['fcstValue'] if evening_sky_data is not None else '1'
94
+ else:
95
+ morning_sky = afternoon_sky = evening_sky = '1'
96
+
97
+ # ์˜ค๋Š˜ ๊ฐ•์ˆ˜ํ˜•ํƒœ ๋ฐ์ดํ„ฐ
98
+ pty_data = today_data[today_data['category'] == 'PTY'].copy()
99
+ if not pty_data.empty:
100
+ pty_data['fcst_datetime'] = pd.to_datetime(pty_data['fcstDate'] + pty_data['fcstTime'], format='%Y%m%d%H00')
101
+ current_pty_data = pty_data.iloc[(pty_data['fcst_datetime'] - current_time).abs().argsort()[:1]]
102
+ current_pty = current_pty_data['fcstValue'].iloc[0] if not current_pty_data.empty else '0'
103
+ else:
104
+ current_pty = '0'
105
+
106
+ # ๋‚ด์ผ ๋ฐ์ดํ„ฐ ๋ถ„์„
107
+ tomorrow_temp_data = tomorrow_data[tomorrow_data['category'] == 'TMP'].copy()
108
+ tomorrow_sky_data = tomorrow_data[tomorrow_data['category'] == 'SKY'].copy()
109
+ tomorrow_pty_data = tomorrow_data[tomorrow_data['category'] == 'PTY'].copy()
110
+
111
+ # ๋‚ด์ผ ์‹œ๊ฐ„๋Œ€๏ฟฝ๏ฟฝ ๊ธฐ์˜จ ๊ณ„์‚ฐ
112
+ tomorrow_temp_data['hour'] = tomorrow_temp_data['fcstTime'].str[:2].astype(int)
113
+ tomorrow_morning_temps = tomorrow_temp_data[tomorrow_temp_data['hour'].between(6, 11)]['fcstValue'].astype(float)
114
+ tomorrow_afternoon_temps = tomorrow_temp_data[tomorrow_temp_data['hour'].between(12, 17)]['fcstValue'].astype(float)
115
+ tomorrow_evening_temps = tomorrow_temp_data[tomorrow_temp_data['hour'].between(18, 23)]['fcstValue'].astype(float)
116
+
117
+ # ๋‚ด์ผ ํ•˜๋Š˜์ƒํƒœ
118
+ if not tomorrow_sky_data.empty:
119
+ tomorrow_sky_data['fcst_datetime'] = pd.to_datetime(tomorrow_sky_data['fcstDate'] + tomorrow_sky_data['fcstTime'], format='%Y%m%d%H00')
120
+ tomorrow_morning_sky = tomorrow_sky_data[tomorrow_sky_data['fcst_datetime'].dt.hour.between(6, 11)].iloc[0]['fcstValue'] if len(tomorrow_sky_data[tomorrow_sky_data['fcst_datetime'].dt.hour.between(6, 11)]) > 0 else '1'
121
+ tomorrow_afternoon_sky = tomorrow_sky_data[tomorrow_sky_data['fcst_datetime'].dt.hour.between(12, 17)].iloc[0]['fcstValue'] if len(tomorrow_sky_data[tomorrow_sky_data['fcst_datetime'].dt.hour.between(12, 17)]) > 0 else '1'
122
+ tomorrow_evening_sky = tomorrow_sky_data[tomorrow_sky_data['fcst_datetime'].dt.hour.between(18, 23)].iloc[0]['fcstValue'] if len(tomorrow_sky_data[tomorrow_sky_data['fcst_datetime'].dt.hour.between(18, 23)]) > 0 else '1'
123
+ else:
124
+ tomorrow_morning_sky = tomorrow_afternoon_sky = tomorrow_evening_sky = '1'
125
+
126
+ # ๋‚ด์ผ ๊ฐ•์ˆ˜์ƒํƒœ
127
+ if not tomorrow_pty_data.empty:
128
+ tomorrow_pty_data['fcst_datetime'] = pd.to_datetime(tomorrow_pty_data['fcstDate'] + tomorrow_pty_data['fcstTime'], format='%Y%m%d%H00')
129
+ tomorrow_morning_pty = tomorrow_pty_data[tomorrow_pty_data['fcst_datetime'].dt.hour.between(6, 11)].iloc[0]['fcstValue'] if len(tomorrow_pty_data[tomorrow_pty_data['fcst_datetime'].dt.hour.between(6, 11)]) > 0 else '0'
130
+ tomorrow_afternoon_pty = tomorrow_pty_data[tomorrow_pty_data['fcst_datetime'].dt.hour.between(12, 17)].iloc[0]['fcstValue'] if len(tomorrow_pty_data[tomorrow_pty_data['fcst_datetime'].dt.hour.between(12, 17)]) > 0 else '0'
131
+ tomorrow_evening_pty = tomorrow_pty_data[tomorrow_pty_data['fcst_datetime'].dt.hour.between(18, 23)].iloc[0]['fcstValue'] if len(tomorrow_pty_data[tomorrow_pty_data['fcst_datetime'].dt.hour.between(18, 23)]) > 0 else '0'
132
+ else:
133
+ tomorrow_morning_pty = tomorrow_afternoon_pty = tomorrow_evening_pty = '0'
134
+
135
+ # ๋‚ ์”จ ์ƒํƒœ ๋งคํ•‘
136
+ sky_status = {'1': '๋ง‘์Œ', '2': '๊ตฌ๋ฆ„์กฐ๊ธˆ', '3': '๊ตฌ๋ฆ„๋งŽ์Œ', '4': 'ํ๋ฆผ'}
137
+ pty_status = {'0': '์—†์Œ', '1': '๋น„', '2': '๋น„/๋ˆˆ', '3': '๋ˆˆ', '4': '์†Œ๋‚˜๊ธฐ'}
138
+
139
+ prompt = f"""
140
+ [์˜ค๋Š˜ ๋‚ ์”จ ์ •๋ณด]
141
+ 1. ๊ธฐ์˜จ ์ •๋ณด:
142
+ - ํ˜„์žฌ ๊ธฐ์˜จ: {current_temp}๋„
143
+ - ์ตœ๊ณ  ๊ธฐ์˜จ: {max(temp_data['fcstValue'].astype(float))}๋„
144
+ - ์ตœ์ € ๊ธฐ์˜จ: {min(temp_data['fcstValue'].astype(float))}๋„
145
+ - ์•„์นจ ํ‰๊ท  ๊ธฐ์˜จ: {morning_temps.mean():.1f}๋„
146
+ - ์˜คํ›„ ํ‰๊ท  ๊ธฐ์˜จ: {afternoon_temps.mean():.1f}๋„
147
+ - ์ €๋… ํ‰๊ท  ๊ธฐ์˜จ: {evening_temps.mean():.1f}๋„
148
+
149
+ 2. ํ•˜๋Š˜์ƒํƒœ:
150
+ - ์•„์นจ: {sky_status.get(morning_sky, '์•Œ์ˆ˜์—†์Œ')}
151
+ - ์˜คํ›„: {sky_status.get(afternoon_sky, '์•Œ์ˆ˜์—†์Œ')}
152
+ - ์ €๋…: {sky_status.get(evening_sky, '์•Œ์ˆ˜์—†์Œ')}
153
+
154
+ 3. ๊ฐ•์ˆ˜ ์ƒํƒœ: {pty_status.get(current_pty, '์•Œ์ˆ˜์—†์Œ')}
155
+
156
+ [๋‚ด์ผ ๋‚ ์”จ ์ •๋ณด]
157
+ 1. ๊ธฐ์˜จ ์ •๋ณด:
158
+ - ์ตœ๊ณ  ๊ธฐ์˜จ: {max(tomorrow_temp_data['fcstValue'].astype(float))}๋„
159
+ - ์ตœ์ € ๊ธฐ์˜จ: {min(tomorrow_temp_data['fcstValue'].astype(float))}๋„
160
+ - ์•„์นจ ํ‰๊ท  ๊ธฐ์˜จ: {tomorrow_morning_temps.mean():.1f}๋„
161
+ - ์˜คํ›„ ํ‰๊ท  ๊ธฐ์˜จ: {tomorrow_afternoon_temps.mean():.1f}๋„
162
+ - ์ €๋… ํ‰๊ท  ๊ธฐ์˜จ: {tomorrow_evening_temps.mean():.1f}๋„
163
+
164
+ 2. ํ•˜๋Š˜์ƒํƒœ:
165
+ - ์•„์นจ: {sky_status.get(tomorrow_morning_sky, '์•Œ์ˆ˜์—†์Œ')}
166
+ - ์˜คํ›„: {sky_status.get(tomorrow_afternoon_sky, '์•Œ์ˆ˜์—†์Œ')}
167
+ - ์ €๋…: {sky_status.get(tomorrow_evening_sky, '์•Œ์ˆ˜์—†์Œ')}
168
+
169
+ 3. ๊ฐ•์ˆ˜ ์ƒํƒœ:
170
+ - ์•„์นจ: {pty_status.get(tomorrow_morning_pty, '์•Œ์ˆ˜์—†์Œ')}
171
+ - ์˜คํ›„: {pty_status.get(tomorrow_afternoon_pty, '์•Œ์ˆ˜์—†์Œ')}
172
+ - ์ €๋…: {pty_status.get(tomorrow_evening_pty, '์•Œ์ˆ˜์—†์Œ')}
173
+
174
+ ์œ„ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์˜ค๋Š˜๊ณผ ๋‚ด์ผ์˜ ๋‚ ์”จ๋ฅผ ๊ฐ๊ฐ 50์ž ๋‚ด์™ธ๋กœ ์ „๋ฌธ ๊ธฐ์ƒ์บ์Šคํ„ฐ์ฒ˜๋Ÿผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.
175
+ "์˜ค๋Š˜ ๋‚ ์”จ:", "๋‚ด์ผ ๋‚ ์”จ:", "๋„ค","์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค."๋“ฑ๊ณผ ๊ฐ™์€ ๋Œ€๋‹ต์€ ์ œ์™ธํ•˜๊ณ ,
176
+ ์˜ค๋Š˜๊ณผ ๋‚ด์ผ ์•„์นจ, ์˜คํ›„, ์ €๋…์˜ ์ตœ๊ณ /์ตœ์ €์˜จ๋„์™€ ๋ˆˆ/๋น„ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜๋“œ์‹œ ํฌํ•จํ•˜๊ณ , ๋‚ ์”จ์ •๋ณด๋งŒ ์˜ค๋Š˜๊ณผ ๋‚ด์ผ ๋‚ ์”จ๋ฅผ ์ด์–ด์„œ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.
177
+ """
178
+
179
+ response = model.generate_content(prompt)
180
+ return response.text
181
+
182
+ def get_current_temperature(temp_data, current_time):
183
+ # fcstTime์„ datetime ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
184
+ temp_data['fcst_datetime'] = pd.to_datetime(temp_data['fcstDate'] + temp_data['fcstTime'], format='%Y%m%d%H00')
185
+
186
+ # ํ˜„์žฌ ์‹œ๊ฐ„๊ณผ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์˜ˆ๋ณด ์‹œ๊ฐ„ ์ฐพ๊ธฐ
187
+ closest_time_data = temp_data.iloc[(temp_data['fcst_datetime'] - current_time).abs().argsort()[:1]]
188
+ return float(closest_time_data['fcstValue'])
189
+
190
+ def main():
191
+ st.title("์‹ค์‹œ๊ฐ„ ๋‚ ์”จ ๋Œ€์‹œ๋ณด๋“œ")
192
+
193
+ # ํŽ˜์ด์ง€ ์—ฌ๋ฐฑ ์ค„์ด๊ธฐ ์œ„ํ•œ CSS ์ถ”๊ฐ€
194
+ st.markdown("""
195
+ <style>
196
+ .block-container {
197
+ padding-left: 1rem !important;
198
+ padding-right: 1rem !important;
199
+ max-width: 95% !important;
200
+ }
201
+ .css-1d391kg {
202
+ padding-left: 1rem;
203
+ padding-right: 1rem;
204
+ }
205
+ </style>
206
+ """, unsafe_allow_html=True)
207
+
208
+ while True:
209
+ try:
210
+ current_time = datetime.now()
211
+ st.sidebar.write(f"๋งˆ์ง€๋ง‰ ์—…๋ฐ์ดํŠธ: {current_time.strftime('%Y-%m-%d %H:%M:%S')}")
212
+
213
+ base_date = current_time.strftime("%Y%m%d")
214
+ xml_data = get_weather_data(base_date)
215
+ df = parse_weather_data(xml_data)
216
+
217
+ # ์˜จ๋„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
218
+ temp_data = df[df['category'] == 'TMP'].copy()
219
+ temp_data['datetime'] = pd.to_datetime(temp_data['fcstDate'] + temp_data['fcstTime'], format='%Y%m%d%H%M')
220
+ temp_data = temp_data.sort_values('datetime')
221
+
222
+ # ํ˜„์žฌ ๊ธฐ์˜จ ๊ณ„์‚ฐ
223
+ current_temp = get_current_temperature(temp_data, current_time)
224
+
225
+ # ํ˜„์žฌ ๊ธฐ์˜จ ํ‘œ์‹œ
226
+ st.sidebar.metric(label="ํ˜„์žฌ ๊ธฐ์˜จ", value=f"{current_temp}ยฐC")
227
+
228
+ # ๋‚ ์”จ ์˜ˆ๋ณด ์ƒ์„ฑ
229
+ weather_forecast = analyze_weather_trends(df)
230
+ st.markdown(f"<span style='font-size: 1.5em;'>{weather_forecast}</span>", unsafe_allow_html=True)
231
+
232
+ # ๊ทธ๋ž˜ํ”„ ๊ทธ๋ฆฌ๊ธฐ
233
+ fig, ax = plt.subplots(figsize=(25, 10))
234
+
235
+ # ์ƒ‰์ƒ ํŒ”๋ ˆํŠธ ์„ค์ •
236
+ colors = ['#FF0000', '#00AA00', '#0000FF', '#FFA500', '#800080'] # ๋นจ๊ฐ•, ์ดˆ๋ก, ํŒŒ๋ž‘, ์ฃผํ™ฉ, ๋ณด๋ผ
237
+
238
+ for idx, date in enumerate(temp_data['fcstDate'].unique()):
239
+ date_data = temp_data[temp_data['fcstDate'] == date]
240
+ temps = date_data['fcstValue'].astype(float)
241
+ times = date_data['datetime']
242
+
243
+ color = colors[idx % len(colors)]
244
+
245
+ # ๊ธฐ๋ณธ ์˜จ๋„ ์„  ๊ทธ๋ž˜ํ”„ - ์„  ์Šคํƒ€์ผ ๊ฐœ์„ 
246
+ line = ax.plot(times, temps,
247
+ marker='o',
248
+ label=f'{date[:4]}-{date[4:6]}-{date[6:]}',
249
+ markersize=12, # ๋งˆ์ปค ํฌ๊ธฐ ์ฆ๊ฐ€
250
+ linewidth=5, # ์„  ๊ตต๊ธฐ ์ฆ๊ฐ€
251
+ color=color,
252
+ markeredgewidth=2.5,
253
+ markerfacecolor=color,
254
+ markeredgecolor='white',
255
+ linestyle='-',
256
+ alpha=0.8) # ์•ฝ๊ฐ„์˜ ํˆฌ๋ช…๋„ ์ถ”๊ฐ€
257
+
258
+ # ์ตœ๊ณ /์ตœ์ € ์˜จ๋„ ์ฐพ๊ธฐ
259
+ max_temp = temps.max()
260
+ min_temp = temps.min()
261
+ max_temp_time = times[temps.idxmax()]
262
+ min_temp_time = times[temps.idxmin()]
263
+
264
+ # ์ตœ๊ณ  ์˜จ๋„ ํ‘œ์‹œ - ๋” ๋ˆˆ์— ๋„๊ฒŒ ์ˆ˜์ •
265
+ ax.annotate(f'์ตœ๊ณ : {max_temp}ยฐC',
266
+ xy=(max_temp_time, max_temp),
267
+ xytext=(10, 15),
268
+ textcoords='offset points',
269
+ ha='left',
270
+ va='bottom',
271
+ bbox=dict(boxstyle='round,pad=0.5',
272
+ fc='yellow',
273
+ alpha=0.7,
274
+ edgecolor=color),
275
+ fontsize=27,
276
+ fontweight='bold',
277
+ color=color)
278
+
279
+ # ์ตœ์ € ์˜จ๋„ ํ‘œ์‹œ - ๋” ๋ˆˆ์— ๋„๊ฒŒ ์ˆ˜์ •
280
+ ax.annotate(f'์ตœ์ €: {min_temp}ยฐC',
281
+ xy=(min_temp_time, min_temp),
282
+ xytext=(10, -15),
283
+ textcoords='offset points',
284
+ ha='left',
285
+ va='top',
286
+ bbox=dict(boxstyle='round,pad=0.5',
287
+ fc='lightblue',
288
+ alpha=0.7,
289
+ edgecolor=color),
290
+ fontsize=27,
291
+ fontweight='bold',
292
+ color=color)
293
+
294
+ # ๊ทธ๋ž˜ํ”„ ์Šคํƒ€์ผ ๊ฐœ์„ 
295
+ ax.set_title('๋‚ ์งœ๋ณ„ ์‹œ๊ฐ„๋Œ€๋ณ„ ๊ธฐ์˜จ ๋ณ€ํ™”', fontsize=18, pad=20, fontweight='bold')
296
+ ax.set_xlabel('๋‚ ์งœ ๋ฐ ์‹œ๊ฐ„', fontsize=16, fontweight='bold')
297
+ ax.set_ylabel('๊ธฐ์˜จ (ยฐC)', fontsize=16, fontweight='bold')
298
+ ax.tick_params(axis='both', labelsize=14)
299
+ ax.grid(True, linestyle='--', alpha=0.4)
300
+
301
+ # ๋ฐค ์‹œ๊ฐ„๋Œ€ ์Œ์˜ ์ฒ˜๋ฆฌ
302
+ dates = temp_data['fcstDate'].unique()
303
+ for date in dates:
304
+ # ๋‹น์ผ 18์‹œ
305
+ evening = pd.to_datetime(f"{date[:4]}-{date[4:6]}-{date[6:]} 18:00:00")
306
+ # ๋‹ค์Œ๋‚  06์‹œ
307
+ next_morning = evening + timedelta(hours=12)
308
+
309
+ # ์Œ์˜ ์ถ”๊ฐ€
310
+ ax.axvspan(evening, next_morning,
311
+ alpha=0.2, # ํˆฌ๋ช…๋„
312
+ color='gray', # ํšŒ์ƒ‰ ์Œ์˜
313
+ label='_nolegend_') # ๋ฒ”๋ก€์—์„œ ์ œ์™ธ
314
+
315
+ # ๋‚ ์งœ ๋ณ€๊ฒฝ์„  ์ถ”๊ฐ€ (00์‹œ ํ‘œ์‹œ)
316
+ dates = temp_data['fcstDate'].unique()
317
+ for date in dates[1:]: # ์ฒซ ๋‚ ์งœ ์ œ์™ธํ•˜๊ณ  ์‹œ์ž‘
318
+ # ํ•ด๋‹น ๋‚ ์งœ์˜ 00์‹œ
319
+ midnight = pd.to_datetime(f"{date[:4]}-{date[4:6]}-{date[6:]} 00:00:00")
320
+
321
+ # ๋‚ ์งœ ๋ณ€๊ฒฝ์„  ์ถ”๊ฐ€
322
+ ax.axvline(x=midnight,
323
+ color='black',
324
+ linestyle=':',
325
+ linewidth=2,
326
+ alpha=0.5,
327
+ label='_nolegend_') # ๋ฒ”๋ก€์—์„œ ์ œ์™ธ
328
+
329
+ # ๋‚ ์งœ ํ‘œ์‹œ
330
+ ax.annotate(f'{date[4:6]}/{date[6:]}',
331
+ xy=(midnight, ax.get_ylim()[1]),
332
+ xytext=(0, 10),
333
+ textcoords='offset points',
334
+ ha='center',
335
+ fontsize=25,
336
+ fontweight='bold',
337
+ color='black')
338
+
339
+ ax.set_facecolor('#f8f9fa') # ๋ฐฐ๊ฒฝ์ƒ‰ ์„ค์ •
340
+
341
+ # ํ˜„์žฌ ์‹œ๊ฐ ์„ธ๋กœ์„  ์ถ”๊ฐ€
342
+ ax.axvline(x=current_time, color='red', linestyle='--', linewidth=3, alpha=0.5)
343
+ ax.annotate('ํ˜„์žฌ',
344
+ xy=(current_time, ax.get_ylim()[1]),
345
+ xytext=(10, 10),
346
+ textcoords='offset points',
347
+ ha='left',
348
+ va='bottom',
349
+ bbox=dict(boxstyle='round,pad=0.5',
350
+ fc='white',
351
+ ec='red',
352
+ alpha=0.8),
353
+ fontsize=25,
354
+ fontweight='bold',
355
+ color='red')
356
+
357
+ # ๋ฒ”๋ก€ ์Šคํƒ€์ผ ์„ค์ • ๊ฐœ์„ 
358
+ legend = ax.legend(fontsize=25,
359
+ frameon=True,
360
+ facecolor='white',
361
+ edgecolor='gray',
362
+ loc='upper right',
363
+ bbox_to_anchor=(1.13, 1.0))
364
+ for text in legend.get_texts():
365
+ text.set_fontweight('bold')
366
+
367
+ plt.xticks(rotation=45)
368
+ # x์ถ• ๋ฒ”์œ„๋ฅผ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ์‹œ๊ฐ„์œผ๋กœ ์ œํ•œ
369
+ min_time = temp_data['datetime'].min()
370
+ max_time = temp_data['datetime'].max()
371
+ ax.set_xlim(min_time, max_time)
372
+ plt.tight_layout()
373
+ st.pyplot(fig, use_container_width=True)
374
+ plt.close()
375
+
376
+ time.sleep(300)
377
+ st.experimental_rerun()
378
+
379
+ except Exception as e:
380
+ st.error(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
381
+ time.sleep(300)
382
+
383
+ if __name__ == "__main__":
384
+ main()