Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| import google.generativeai as genai | |
| from datetime import datetime, timedelta | |
| import time | |
| import xml.etree.ElementTree as ET | |
| import requests | |
| import matplotlib.font_manager as fm | |
| import os | |
| # ν°νΈ μ€μ | |
| plt.rcParams['font.family'] = 'DejaVu Sans' | |
| plt.rcParams['axes.unicode_minus'] = False | |
| # API ν€ μ€μ | |
| GEMINI_API_KEY = os.environ.get('GEMINI_API_KEY') | |
| WEATHER_API_KEY = os.environ.get('WEATHER_API_KEY') | |
| # Gemini λͺ¨λΈ μ€μ | |
| genai.configure(api_key=GEMINI_API_KEY) | |
| model = genai.GenerativeModel('gemini-2.0-flash-exp') | |
| def get_weather_data(base_date): | |
| url = "https://apihub.kma.go.kr/api/typ02/openApi/VilageFcstInfoService_2.0/getVilageFcst" | |
| params = { | |
| 'pageNo': '1', | |
| 'numOfRows': '1000', | |
| 'dataType': 'XML', | |
| 'base_date': base_date, | |
| 'base_time': '0500', | |
| 'nx': '59', | |
| 'ny': '125', | |
| 'authKey': WEATHER_API_KEY | |
| } | |
| response = requests.get(url, params=params) | |
| response.raise_for_status() | |
| return response.text | |
| def parse_weather_data(xml_data): | |
| root = ET.fromstring(xml_data) | |
| all_data = [] | |
| for item in root.findall('.//item'): | |
| all_data.append({ | |
| 'base_date': item.find('baseDate').text, | |
| 'category': item.find('category').text, | |
| 'fcstTime': item.find('fcstTime').text, | |
| 'fcstValue': item.find('fcstValue').text, | |
| 'fcstDate': item.find('fcstDate').text | |
| }) | |
| return pd.DataFrame(all_data) | |
| def get_precipitation_times(pty_data): | |
| if pty_data.empty: | |
| return "μμ" | |
| # κ°μκ° μλ μκ°λ μ°ΎκΈ° | |
| precipitation_times = pty_data[pty_data['fcstValue'] != '0'] | |
| if precipitation_times.empty: | |
| return "μμ" | |
| # μ°μλ μκ°λ κ·Έλ£Ήν | |
| times = precipitation_times['fcst_datetime'].dt.strftime('%Hμ') | |
| return f"{times.iloc[0]}λΆν° {times.iloc[-1]}κΉμ§" | |
| def analyze_weather_trends(df, current_time): | |
| tomorrow = (current_time + timedelta(days=1)).strftime("%Y%m%d") | |
| # μ€λκ³Ό λ΄μΌ λ μ§μ λ°μ΄ν° λΆλ¦¬ | |
| today_data = df[df['fcstDate'] == current_time.strftime("%Y%m%d")] | |
| tomorrow_data = df[df['fcstDate'] == tomorrow] | |
| # μ€λ κΈ°μ¨ λ°μ΄ν° μ²λ¦¬ | |
| temp_data = today_data[today_data['category'] == 'TMP'].copy() | |
| if not temp_data.empty: | |
| temp_data['fcst_datetime'] = pd.to_datetime(temp_data['fcstDate'] + temp_data['fcstTime'], format='%Y%m%d%H00') | |
| closest_time_data = temp_data.iloc[(temp_data['fcst_datetime'] - current_time).abs().argsort()[:1]] | |
| current_temp = float(closest_time_data['fcstValue'].iloc[0]) | |
| else: | |
| current_temp = 0 | |
| # μ€λ μκ°λλ³ κΈ°μ¨ κ³μ° | |
| temp_data['hour'] = temp_data['fcstTime'].str[:2].astype(int) | |
| morning_temps = temp_data[temp_data['hour'].between(6, 11)]['fcstValue'].astype(float) | |
| afternoon_temps = temp_data[temp_data['hour'].between(12, 17)]['fcstValue'].astype(float) | |
| evening_temps = temp_data[temp_data['hour'].between(18, 23)]['fcstValue'].astype(float) | |
| # μ€λ νλμν λ°μ΄ν° | |
| sky_data = today_data[today_data['category'] == 'SKY'].copy() | |
| if not sky_data.empty: | |
| sky_data['fcst_datetime'] = pd.to_datetime(sky_data['fcstDate'] + sky_data['fcstTime'], format='%Y%m%d%H00') | |
| 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 | |
| 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 | |
| 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 | |
| morning_sky = morning_sky_data['fcstValue'] if morning_sky_data is not None else '1' | |
| afternoon_sky = afternoon_sky_data['fcstValue'] if afternoon_sky_data is not None else '1' | |
| evening_sky = evening_sky_data['fcstValue'] if evening_sky_data is not None else '1' | |
| else: | |
| morning_sky = afternoon_sky = evening_sky = '1' | |
| # μ€λ κ°μνν λ°μ΄ν° | |
| pty_data = today_data[today_data['category'] == 'PTY'].copy() | |
| if not pty_data.empty: | |
| pty_data['fcst_datetime'] = pd.to_datetime(pty_data['fcstDate'] + pty_data['fcstTime'], format='%Y%m%d%H00') | |
| current_pty_data = pty_data.iloc[(pty_data['fcst_datetime'] - current_time).abs().argsort()[:1]] | |
| current_pty = current_pty_data['fcstValue'].iloc[0] if not current_pty_data.empty else '0' | |
| today_precip_times = get_precipitation_times(pty_data) | |
| else: | |
| current_pty = '0' | |
| today_precip_times = "μμ" | |
| # λ΄μΌ λ°μ΄ν° λΆμ | |
| tomorrow_temp_data = tomorrow_data[tomorrow_data['category'] == 'TMP'].copy() | |
| tomorrow_sky_data = tomorrow_data[tomorrow_data['category'] == 'SKY'].copy() | |
| tomorrow_pty_data = tomorrow_data[tomorrow_data['category'] == 'PTY'].copy() | |
| if not tomorrow_pty_data.empty: | |
| tomorrow_pty_data['fcst_datetime'] = pd.to_datetime(tomorrow_pty_data['fcstDate'] + tomorrow_pty_data['fcstTime'], format='%Y%m%d%H00') | |
| tomorrow_precip_times = get_precipitation_times(tomorrow_pty_data) | |
| else: | |
| tomorrow_precip_times = "μμ" | |
| # λ΄μΌ μκ°λλ³ κΈ°μ¨ κ³μ° | |
| tomorrow_temp_data['hour'] = tomorrow_temp_data['fcstTime'].str[:2].astype(int) | |
| tomorrow_morning_temps = tomorrow_temp_data[tomorrow_temp_data['hour'].between(6, 11)]['fcstValue'].astype(float) | |
| tomorrow_afternoon_temps = tomorrow_temp_data[tomorrow_temp_data['hour'].between(12, 17)]['fcstValue'].astype(float) | |
| tomorrow_evening_temps = tomorrow_temp_data[tomorrow_temp_data['hour'].between(18, 23)]['fcstValue'].astype(float) | |
| # λ΄μΌ νλμν | |
| if not tomorrow_sky_data.empty: | |
| tomorrow_sky_data['fcst_datetime'] = pd.to_datetime(tomorrow_sky_data['fcstDate'] + tomorrow_sky_data['fcstTime'], format='%Y%m%d%H00') | |
| 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' | |
| 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' | |
| 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' | |
| else: | |
| tomorrow_morning_sky = tomorrow_afternoon_sky = tomorrow_evening_sky = '1' | |
| # λ΄μΌ κ°μμν | |
| if not tomorrow_pty_data.empty: | |
| 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' | |
| 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' | |
| 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' | |
| else: | |
| tomorrow_morning_pty = tomorrow_afternoon_pty = tomorrow_evening_pty = '0' | |
| # λ μ¨ μν λ§€ν | |
| sky_status = {'1': 'λ§μ', '2': 'ꡬλ¦μ‘°κΈ', '3': 'ꡬλ¦λ§μ', '4': 'νλ¦Ό'} | |
| pty_status = {'0': 'μμ', '1': 'λΉ', '2': 'λΉ/λ', '3': 'λ', '4': 'μλκΈ°'} | |
| prompt = f""" | |
| νμ¬ μκ°: {current_time.strftime('%Yλ %mμ %dμΌ %Hμ %MλΆ')} κΈ°μ€ | |
| [μ€λ λ μ¨ μ 보] | |
| 1. κΈ°μ¨ μ 보: | |
| - νμ¬ κΈ°μ¨: {current_temp}λ | |
| - μ΅κ³ κΈ°μ¨: {max(temp_data['fcstValue'].astype(float))}λ | |
| - μ΅μ κΈ°μ¨: {min(temp_data['fcstValue'].astype(float))}λ | |
| - μμΉ¨ νκ· κΈ°μ¨: {morning_temps.mean():.1f}λ | |
| - μ€ν νκ· κΈ°μ¨: {afternoon_temps.mean():.1f}λ | |
| - μ λ νκ· κΈ°μ¨: {evening_temps.mean():.1f}λ | |
| 2. νλμν: | |
| - μμΉ¨: {sky_status.get(morning_sky, 'μμμμ')} | |
| - μ€ν: {sky_status.get(afternoon_sky, 'μμμμ')} | |
| - μ λ : {sky_status.get(evening_sky, 'μμμμ')} | |
| 3. κ°μ μν: {pty_status.get(current_pty, 'μμμμ')} | |
| κ°μ μκ°: {today_precip_times} | |
| [λ΄μΌ λ μ¨ μ 보] | |
| 1. κΈ°μ¨ μ 보: | |
| - μ΅κ³ κΈ°μ¨: {max(tomorrow_temp_data['fcstValue'].astype(float))}λ | |
| - μ΅μ κΈ°μ¨: {min(tomorrow_temp_data['fcstValue'].astype(float))}λ | |
| - μμΉ¨ νκ· κΈ°μ¨: {tomorrow_morning_temps.mean():.1f}λ | |
| - μ€ν νκ· κΈ°μ¨: {tomorrow_afternoon_temps.mean():.1f}λ | |
| - μ λ νκ· κΈ°μ¨: {tomorrow_evening_temps.mean():.1f}λ | |
| 2. νλμν: | |
| - μμΉ¨: {sky_status.get(tomorrow_morning_sky, 'μμμμ')} | |
| - μ€ν: {sky_status.get(tomorrow_afternoon_sky, 'μμμμ')} | |
| - μ λ : {sky_status.get(tomorrow_evening_sky, 'μμμμ')} | |
| 3. κ°μ μν: | |
| - μμΉ¨: {pty_status.get(tomorrow_morning_pty, 'μμμμ')} | |
| - μ€ν: {pty_status.get(tomorrow_afternoon_pty, 'μμμμ')} | |
| - μ λ : {pty_status.get(tomorrow_evening_pty, 'μμμμ')} | |
| κ°μ μκ°: {tomorrow_precip_times} | |
| μ μ 보λ₯Ό λ°νμΌλ‘ μ€λκ³Ό λ΄μΌμ λ μ¨λ₯Ό μ΄ 100μ μ λλ‘ μ λ¬Έ κΈ°μμΊμ€ν°μ²λΌ μμ°μ€λ½κ² μ€λͺ ν΄μ£ΌμΈμ. | |
| "λ€","μ€λͺ λλ¦¬κ² μ΅λλ€."λ±κ³Ό κ°μ μΈμ¬λ§μ μ μΈνκ³ , λ μ¨μ λ³΄λ§ μ€λκ³Ό λ΄μΌ λ μ¨λ₯Ό μ΄μ΄μ μ€λͺ ν΄μ£ΌμΈμ. | |
| νμ¬μκ° κΈ°μ¨μ λ¨Όμ μλ €μ£Όκ³ , μ€λκ³Ό λ΄μΌ μμΉ¨, μ€ν, μ λ μ μ΅κ³ /μ΅μ μ¨λμ λ/λΉ μ¬λΆλ₯Ό λ°λμ ν¬ν¨νκ³ , | |
| λμ΄λ λΉκ° μ€λ©΄ λͺμλΆν° λͺμκΉμ§ μ€λμ§ λ°λμ μλ €μ£ΌμΈμ. | |
| """ | |
| response = model.generate_content(prompt) | |
| return response.text | |
| def get_current_temperature(temp_data, current_time): | |
| # fcstTimeμ datetime κ°μ²΄λ‘ λ³ν | |
| temp_data['fcst_datetime'] = pd.to_datetime(temp_data['fcstDate'] + temp_data['fcstTime'], format='%Y%m%d%H00') | |
| # νμ¬ μκ°κ³Ό κ°μ₯ κ°κΉμ΄ μ보 μκ° μ°ΎκΈ° | |
| closest_time_data = temp_data.iloc[(temp_data['fcst_datetime'] - current_time).abs().argsort()[:1]] | |
| return float(closest_time_data['fcstValue']) | |
| def main(): | |
| # νμ΄μ§ μ¬λ°± μ€μ΄κΈ° μν CSS μΆκ° | |
| st.markdown(""" | |
| <style> | |
| .block-container { | |
| padding-left: 1rem !important; | |
| padding-right: 1rem !important; | |
| max-width: 95% !important; | |
| } | |
| .css-1d391kg { | |
| padding-left: 1rem; | |
| padding-right: 1rem; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| while True: | |
| try: | |
| current_time = datetime.now() + timedelta(hours=8) | |
| print(f"[DEBUG] Current time: {current_time}") | |
| base_date = current_time.strftime("%Y%m%d") | |
| print(f"[DEBUG] Base date: {base_date}") | |
| # 05μ μ΄μ μ΄λ©΄ μ λ λ°μ΄ν° μ¬μ© | |
| if current_time.hour < 5: | |
| base_date = (current_time - timedelta(days=1)).strftime("%Y%m%d") | |
| else: | |
| base_date = current_time.strftime("%Y%m%d") | |
| print(f"[DEBUG] Base date for API request: {base_date}") | |
| xml_data = get_weather_data(base_date) | |
| print(f"[DEBUG] XML data received: {len(xml_data)} bytes") | |
| print(f"[DEBUG] XML content: {xml_data[:500]}") # μ²μ 500μλ§ μΆλ ₯ | |
| # XML νμ± μ μ root μμ νμΈ | |
| root = ET.fromstring(xml_data) | |
| print(f"[DEBUG] XML root tag: {root.tag}") | |
| print(f"[DEBUG] XML root children: {[child.tag for child in root]}") | |
| df = parse_weather_data(xml_data) | |
| print(f"[DEBUG] Parsed DataFrame shape: {df.shape}") | |
| print(f"[DEBUG] DataFrame columns: {df.columns}") | |
| # μ¨λ λ°μ΄ν° μ²λ¦¬ | |
| temp_data = df[df['category'] == 'TMP'].copy() | |
| print(f"[DEBUG] Temperature data shape: {temp_data.shape}") | |
| temp_data['datetime'] = pd.to_datetime(temp_data['fcstDate'] + temp_data['fcstTime'], format='%Y%m%d%H%M') | |
| print(f"[DEBUG] Datetime range: {temp_data['datetime'].min()} to {temp_data['datetime'].max()}") | |
| # νμ¬ κΈ°μ¨ κ³μ° | |
| current_temp = get_current_temperature(temp_data, current_time) | |
| print(f"[DEBUG] Current temperature: {current_temp}") | |
| # λ μ¨ μ보 μμ± | |
| weather_forecast = analyze_weather_trends(df, current_time) | |
| print(f"[DEBUG] Weather forecast generated: {len(weather_forecast)} characters") | |
| # λ΄μΌ μμΉ¨ κΈ°μ¨ κ³μ° | |
| tomorrow_data = df[df['fcstDate'] == (current_time + timedelta(days=1)).strftime("%Y%m%d")] | |
| tomorrow_temp_data = tomorrow_data[tomorrow_data['category'] == 'TMP'].copy() | |
| tomorrow_temp_data['hour'] = tomorrow_temp_data['fcstTime'].str[:2].astype(int) | |
| tomorrow_morning_temps = tomorrow_temp_data[tomorrow_temp_data['hour'].between(6, 11)]['fcstValue'].astype(float) | |
| tomorrow_morning_temp = tomorrow_temp_data[tomorrow_temp_data['hour'] == 6]['fcstValue'].iloc[0] if not tomorrow_temp_data[tomorrow_temp_data['hour'] == 6].empty else 0 | |
| st.markdown(f"""<span style='font-size: 3.5em'><b>μ€μκ° λ μ¨ λμ보λ</b> <span style='font-size: 0.7em'>(νμ¬μ¨λ: <b><span style='color: {'red' if current_temp >= 0 else 'blue'}'>{current_temp:.1f}</span></b>λ, λ΄μΌ μμΉ¨ 6μ μ¨λ: <b><span style='color: {'red' if float(tomorrow_morning_temp) >= 0 else 'blue'}'>{float(tomorrow_morning_temp):.1f}</span></b>λ)</span></span>""", unsafe_allow_html=True) | |
| # κ·Έλν 그리기 | |
| fig, ax = plt.subplots(figsize=(25, 10)) | |
| # μμ νλ νΈ μ€μ | |
| colors = ['#FF0000', '#00AA00', '#0000FF', '#FFA500', '#800080'] # λΉ¨κ°, μ΄λ‘, νλ, μ£Όν©, λ³΄λΌ | |
| for idx, date in enumerate(temp_data['fcstDate'].unique()): | |
| date_data = temp_data[temp_data['fcstDate'] == date] | |
| temps = date_data['fcstValue'].astype(float) | |
| times = date_data['datetime'] | |
| color = colors[idx % len(colors)] | |
| # κΈ°λ³Έ μ¨λ μ κ·Έλν - μ μ€νμΌ κ°μ | |
| line = ax.plot(times, temps, | |
| marker='o', | |
| label=f'{date[:4]}-{date[4:6]}-{date[6:]}', | |
| markersize=12, # λ§μ»€ ν¬κΈ° μ¦κ° | |
| linewidth=5, # μ κ΅΅κΈ° μ¦κ° | |
| color=color, | |
| markeredgewidth=2.5, | |
| markerfacecolor=color, | |
| markeredgecolor='white', | |
| linestyle='-', | |
| alpha=0.8) # μ½κ°μ ν¬λͺ λ μΆκ° | |
| # μ΅κ³ /μ΅μ μ¨λ μ°ΎκΈ° | |
| max_temp = temps.max() | |
| min_temp = temps.min() | |
| max_temp_time = times[temps.idxmax()] | |
| min_temp_time = times[temps.idxmin()] | |
| # μ΅κ³ μ¨λ νμ - λ λμ λκ² μμ | |
| ax.annotate(f'Max: {max_temp}Β°C', | |
| xy=(max_temp_time, max_temp), | |
| xytext=(10, 15), | |
| textcoords='offset points', | |
| ha='left', | |
| va='bottom', | |
| bbox=dict(boxstyle='round,pad=0.5', | |
| fc='yellow', | |
| alpha=0.7, | |
| edgecolor=color), | |
| fontsize=27, | |
| fontweight='bold', | |
| color=color) | |
| # μ΅μ μ¨λ νμ - λ λμ λκ² μμ | |
| ax.annotate(f'Min: {min_temp}Β°C', | |
| xy=(min_temp_time, min_temp), | |
| xytext=(10, -15), | |
| textcoords='offset points', | |
| ha='left', | |
| va='top', | |
| bbox=dict(boxstyle='round,pad=0.5', | |
| fc='lightblue', | |
| alpha=0.7, | |
| edgecolor=color), | |
| fontsize=27, | |
| fontweight='bold', | |
| color=color) | |
| # κ·Έλν μ€νμΌ κ°μ | |
| ax.set_title('', fontsize=18, pad=20, fontweight='bold') | |
| ax.set_xlabel('λ μ§ λ° μκ°', fontsize=16, fontweight='bold') | |
| ax.set_ylabel('κΈ°μ¨ (Β°C)', fontsize=16, fontweight='bold') | |
| ax.tick_params(axis='both', labelsize=14) | |
| ax.grid(True, linestyle='--', alpha=0.4) | |
| # λ°€ μκ°λ μμ μ²λ¦¬ | |
| dates = temp_data['fcstDate'].unique() | |
| for date in dates: | |
| evening = pd.to_datetime(f"{date[:4]}-{date[4:6]}-{date[6:]} 18:00:00") | |
| next_morning = evening + timedelta(hours=12) | |
| ax.axvspan(evening, next_morning, | |
| alpha=0.2, | |
| color='gray', | |
| label='_nolegend_') | |
| # λ μ§ λ³κ²½μ μΆκ° | |
| dates = temp_data['fcstDate'].unique() | |
| for date in dates[1:]: | |
| midnight = pd.to_datetime(f"{date[:4]}-{date[4:6]}-{date[6:]} 00:00:00") | |
| ax.axvline(x=midnight, | |
| color='black', | |
| linestyle=':', | |
| linewidth=2, | |
| alpha=0.5, | |
| label='_nolegend_') | |
| ax.annotate(f'{date[4:6]}/{date[6:]}', | |
| xy=(midnight, ax.get_ylim()[1]), | |
| xytext=(0, 10), | |
| textcoords='offset points', | |
| ha='center', | |
| fontsize=25, | |
| fontweight='bold', | |
| color='black') | |
| ax.set_facecolor('#f8f9fa') | |
| min_time = temp_data['datetime'].min() | |
| max_time = temp_data['datetime'].max() | |
| # νμ¬ μκ° μΈλ‘μ μΆκ° | |
| current_datetime = pd.to_datetime(current_time) | |
| if min_time <= current_datetime <= max_time: | |
| ax.axvline(x=current_datetime, color='red', linestyle='--', linewidth=3, alpha=0.7) | |
| ax.annotate('Now', | |
| xy=(current_datetime, ax.get_ylim()[1]), | |
| xytext=(0, 10), | |
| textcoords='offset points', | |
| ha='center', | |
| va='bottom', | |
| bbox=dict(boxstyle='round,pad=0.5', | |
| fc='white', | |
| ec='red', | |
| alpha=0.8), | |
| fontsize=25, | |
| fontweight='bold', | |
| color='red') | |
| ax.set_xlim(min_time, max_time) | |
| # λ²λ‘ μ€νμΌ μ€μ κ°μ | |
| legend = ax.legend(fontsize=25, | |
| frameon=True, | |
| facecolor='white', | |
| edgecolor='gray', | |
| loc='upper right', | |
| bbox_to_anchor=(1.13, 1.0)) | |
| for text in legend.get_texts(): | |
| text.set_fontweight('bold') | |
| plt.xticks(rotation=45) | |
| plt.tight_layout() | |
| st.pyplot(fig, use_container_width=True) | |
| plt.close() | |
| # λ μ¨ μ보 ν μ€νΈλ₯Ό μ€ν¬λ‘€ ν¨κ³Όμ ν¨κ» νμ | |
| st.markdown( | |
| '<style>' | |
| '.scroll-container {' | |
| ' width: 100%;' | |
| ' overflow: hidden;' | |
| ' padding: 10px 0;' | |
| '}' | |
| '.scroll-text {' | |
| ' display: inline-block;' | |
| ' white-space: nowrap;' | |
| ' animation: scroll-left 40s linear infinite;' | |
| ' padding-left: 100%;' | |
| '}' | |
| '@keyframes scroll-left {' | |
| ' 0% { transform: translateX(0); }' | |
| ' 100% { transform: translateX(-100%); }' # -200%μμ -100%λ‘ λ³κ²½ | |
| '}' | |
| '</style>' | |
| '<div class="scroll-container">' | |
| f' <div class="scroll-text">' | |
| f' <span style="font-size: 1.8em; font-weight: bold; color: yellow;">{weather_forecast.replace(chr(10), " ")}</span>' | |
| f' <span style="font-size: 1.8em; font-weight: bold; color: yellow; margin-left: 100px;">{weather_forecast.replace(chr(10), " ")}</span>' # λμΌν ν μ€νΈ νλ² λ μΆκ° | |
| ' </div>' | |
| '</div>', | |
| unsafe_allow_html=True | |
| ) | |
| time.sleep(300) | |
| st.rerun() | |
| except Exception as e: | |
| st.error(f"μ€λ₯ λ°μ: {str(e)}") | |
| time.sleep(300) | |
| if __name__ == "__main__": | |
| main() | |