import openmeteo_requests import requests_cache import polars as pl from retry_requests import retry import streamlit as st from datetime import datetime, timedelta import pytz from lets_plot import * from streamlit_letsplot import st_letsplot import numpy as np #API Fetch openmeteo = openmeteo_requests.Client() #Empty Frames locations = [ {"name": "Rexburg, Idaho", "latitude": 43.8251, "longitude": -111.7924}, {"name": "Provo, Utah", "latitude": 40.2338, "longitude": -111.6585}, {"name": "Laie, Hawaii", "latitude": 21.6210, "longitude": -157.9753} ] location_names = [location["name"] for location in locations] filtered_forecasts = {} filtered_histories = {} historical_data = [] forecast_data = [] timezones = pytz.all_timezones def find_max(df): daily_highs = df.group_by(by = 'Date').agg(pl.max("Temperature").alias('max')) return daily_highs, df # Date Variables today = datetime.today() default = today - timedelta(days=14) # Streamlit Variables start_date = st.sidebar.date_input( 'Select start date:', value = default, max_value= default ) selected_timezone = st.sidebar.selectbox( 'Choose a timezone:', timezones ) end_date = start_date + timedelta(days=14) temperature_option = st.sidebar.selectbox( 'Select Temperature Type', ('Highest', 'Lowest') ) city_option = st.sidebar.selectbox( 'Select City', ('Rexburg, Idaho', 'Provo, Utah', 'Laie, Hawaii') ) # Display the selected date range st.sidebar.write(f"Date Range: {start_date} to {end_date}") # Get Forecast Data for location in locations: url = "https://api.open-meteo.com/v1/forecast" params = { "latitude": location["latitude"], "longitude": location["longitude"], "start_date": start_date.strftime('%Y-%m-%d'), "end_date": end_date.strftime('%Y-%m-%d'), "hourly": "temperature_2m" } # Fetch weather data responses = openmeteo.weather_api(url, params=params) response = responses[0] hourly = response.Hourly() hourly_temperature_2m = hourly.Variables(0).ValuesAsNumpy() # Convert timestamp to datetime start = datetime.fromtimestamp(hourly.Time()) end = datetime.fromtimestamp(hourly.TimeEnd()) freq = timedelta(seconds=hourly.Interval()) # Create Polars DataFrame hourly_data = pl.select( date = pl.datetime_range(start, end, freq, closed = "left"), temperature_2m = hourly_temperature_2m, location = [location["name"]]) hourly_dataframe = pl.DataFrame(data = hourly_data) historical_data.append(hourly_dataframe) # Concatenate all DataFrames combined_historical = pl.DataFrame(pl.concat(historical_data)).explode('location') # Get True Historical Data for location in locations: url = "https://archive-api.open-meteo.com/v1/archive" params = { "latitude": location["latitude"], "longitude": location["longitude"], "start_date": start_date.strftime('%Y-%m-%d'), "end_date": end_date.strftime('%Y-%m-%d'), "hourly": "temperature_2m" } # Fetch weather data responses = openmeteo.weather_api(url, params=params) response = responses[0] hourly = response.Hourly() hourly_temperature_2m = hourly.Variables(0).ValuesAsNumpy() # Convert timestamp to datetime start = datetime.fromtimestamp(hourly.Time()) end = datetime.fromtimestamp(hourly.TimeEnd()) freq = timedelta(seconds=hourly.Interval()) # Create Polars DataFrame hourly_data = pl.select( date = pl.datetime_range(start, end, freq, closed = "left"), temperature_2m = hourly_temperature_2m, location = [location["name"]]) hourly_dataframe = pl.DataFrame(data = hourly_data) forecast_data.append(hourly_dataframe) combined_forecast = pl.DataFrame(pl.concat(forecast_data)).explode('location') for name in location_names: filtered_forecasts[name] = (combined_forecast.filter(pl.col("location") == name).drop('location').rename({'date': 'Date'}).rename({'temperature_2m': 'Temperature'}).with_columns(pl.col('Temperature') * 9/5 + 32)) filtered_histories[name] = (combined_historical.filter(pl.col("location") == name).drop('location').rename({'date': 'Date'}).rename({'temperature_2m': 'Temperature'}).with_columns(pl.col('Temperature') * 9/5 + 32)) tab1, tab2, tab3 = st.tabs(["Data", "Visualisations", "KPIs"]) with tab1: st.title("Forecasted Weather vs Actual Weather by City") st.header('Forecasts') st.markdown("

Rexburg

", unsafe_allow_html=True) # Create two columns for Rexburg content rexburg_col1, rexburg_col2 = st.columns(2) # Rexburg content with rexburg_col1: st.write("Forecasts") st.dataframe(filtered_forecasts["Rexburg, Idaho"], use_container_width=True, hide_index=True) with rexburg_col2: st.write("Historical Data") st.dataframe(filtered_histories["Rexburg, Idaho"], use_container_width=True, hide_index=True) # Provo Header st.markdown("

Provo

", unsafe_allow_html=True) # Create two columns for Provo content provo_col1, provo_col2 = st.columns(2) # Provo content with provo_col1: st.write("Forecasts") st.dataframe(filtered_forecasts["Provo, Utah"], use_container_width=True, hide_index=True) with provo_col2: st.write("Historical Data") st.dataframe(filtered_histories["Provo, Utah"], use_container_width=True, hide_index=True) # Laie Header st.markdown("

Laie

", unsafe_allow_html=True) # Create two columns for Laie content laie_col1, laie_col2 = st.columns(2) # Laie content with laie_col1: st.write("Forecasts") st.dataframe(filtered_forecasts["Laie, Hawaii"], use_container_width=True, hide_index=True) with laie_col2: st.write("Historical Data") st.dataframe(filtered_histories["Laie, Hawaii"], use_container_width=True, hide_index=True) with tab2: st.header('Visualisations by City') st.subheader("Forecasted Data vs. Historical Data") combined_forecast = combined_forecast.rename({'date': 'Date'}).rename({'temperature_2m': 'Temperature'}).with_columns(pl.col('Temperature') * 9/5 + 32).with_columns(pl.col('Date').cast(datetime)) combined_historical = combined_historical.rename({'date': 'Date'}).rename({'temperature_2m': 'Temperature'}).with_columns(pl.col('Temperature') * 9/5 + 32).with_columns(pl.col('Date').cast(datetime)) reg_forecasted = ggplot(combined_forecast, aes(x='Date', y='Temperature', color = 'location')) \ + geom_line() \ + facet_wrap('location', ncol = 1) \ + labs(title = f'Hourly Temperatures', x = 'Date', y = 'Temperature (°F)', color = 'City Name') + \ guides(color="none") + \ scale_x_datetime(format = '%m/%d') reg_historical = ggplot(combined_historical, aes(x='Date', y='Temperature', color = 'location')) \ + geom_line() \ + facet_wrap('location', ncol = 1) \ + labs(title = f'Hourly Temperatures', x = 'Date', y = 'Temperature (°F)', color = 'City Name') + \ guides(color="none") + \ scale_x_datetime(format = '%m/%d') st.subheader('Forecasted') st_letsplot(reg_forecasted) st.subheader('Historical') st_letsplot(reg_historical) st.subheader('Boxplot of Average Hourly Temperature') box_plot = ggplot(combined_forecast, aes(x='location', y='Temperature', color = 'location')) + \ geom_jitter(alpha = .65) + \ geom_boxplot(alpha = .9) + \ labs(title=f'Hourly Temperature Readings', x='City', y='Temperature (°F)') + \ guides(color = "none") st_letsplot(box_plot) maxes = combined_historical.group_by('location').agg((pl.col('Temperature').max())) max_plot = ggplot(maxes, aes(x = 'location', y = 'Temperature', color = 'location')) + \ geom_bar(stat='identity') + \ labs(x = 'City Name', y = 'Max Temperature in Month (°F)') st.subheader('Maximum Temperature for Period') st_letsplot(max_plot) with tab3: if temperature_option == 'Highest': highest_temp = combined_historical.filter(pl.col('location') == city_option).select(pl.max('Temperature')).item() st.metric(label=f"Highest Temperature in {city_option}", value=f"{round(highest_temp,2)} °F") else: # Get the lowest temperature and its corresponding date lowest_temp = combined_historical.filter(pl.col('location') == city_option).select(pl.min('Temperature')).item() st.metric(label=f"Highest Temperature in {city_option}", value=f"{round(lowest_temp,2)} °F")