Spaces:
Sleeping
Sleeping
Create app.py
Browse files
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()
|