Spaces:
Runtime error
Runtime error
File size: 7,001 Bytes
d4bea00 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
import streamlit as st
import requests
import json
import pandas as pd
import folium
from streamlit_folium import st_folium
import plotly.graph_objects as go
import numpy as np
from datetime import datetime, timezone
import time
import pytz
# Set page layout to wide
st.set_page_config(layout="wide", page_title="Real-Time Temperature Data Dashboard")
# Function to fetch JSON data with caching and expiration
@st.cache_data(ttl=300) # Cache data for 5 minutes (300 seconds)
def fetch_data():
url = 'https://csdi.vercel.app/weather/temp'
response = requests.get(url)
hk_tz = pytz.timezone('Asia/Hong_Kong')
fetch_time = datetime.now(hk_tz).strftime('%Y-%m-%dT%H:%M:%S')
return json.loads(response.text), fetch_time
# Fetch the JSON data
data, fetch_time = fetch_data()
# Create a Pandas DataFrame from the JSON data
features = data['features']
df = pd.json_normalize(features)
# Rename columns for easier access
df.rename(columns={
'properties.Automatic Weather Station': 'Station',
'properties.Air Temperature(degree Celsius)': 'Temperature',
'geometry.coordinates': 'Coordinates'
}, inplace=True)
# Split Coordinates into separate Longitude and Latitude columns
df[['Longitude', 'Latitude']] = pd.DataFrame(df['Coordinates'].tolist(), index=df.index)
# Extract temperature data
temps = df['Temperature'].dropna().tolist()
# Create three columns
col1, col2, col3 = st.columns([1.65, 2, 1.15])
# Column 1: Histogram and statistics with two-sigma analysis
with col1:
# Row 1: Histogram
with st.container():
# Convert list to pandas Series
temps_series = pd.Series(temps)
# Calculate histogram data
hist_data = np.histogram(temps_series, bins=10)
bin_edges = hist_data[1]
counts = hist_data[0]
# Create a color gradient from blue to red
def get_color(value, min_value, max_value):
ratio = (value - min_value) / (max_value - min_value)
r = int(255 * ratio) # Red component
b = int(255 * (1 - ratio)) # Blue component
return f'rgb({r}, 0, {b})'
# Create histogram with Plotly Graph Objects
fig = go.Figure()
# Add histogram bars with gradient colors
for i in range(len(bin_edges) - 1):
bin_center = (bin_edges[i] + bin_edges[i + 1]) / 2
color = get_color(bin_center, bin_edges.min(), bin_edges.max())
fig.add_trace(go.Bar(
x=[f'{bin_edges[i]:.1f} - {bin_edges[i + 1]:.1f}'],
y=[counts[i]],
marker_color=color,
name=f'{bin_edges[i]:.1f} - {bin_edges[i + 1]:.1f}'
))
# Customize layout
fig.update_layout(
xaxis_title='Temperature (°C)',
yaxis_title='Count',
title='Temperature Distribution',
bargap=0.2, # Adjust gap between bars
title_font_size=20,
xaxis_title_font_size=14,
yaxis_title_font_size=14,
height=350, # Set plot height
xaxis=dict(title_font_size=14),
yaxis=dict(title_font_size=14)
)
# Display the plot in Streamlit
st.plotly_chart(fig, use_container_width=True)
st.caption(f"Data fetched at: {fetch_time}")
# Row 2: Statistics
with st.container():
col_1, col_2 = st.columns([1, 1])
with col_1:
if temps:
avg_temp = np.mean(temps)
std_temp = np.std(temps)
max_temp = np.max(temps)
min_temp = np.min(temps)
two_sigma_range = (avg_temp - 2 * std_temp, avg_temp + 2 * std_temp)
st.metric(label="Average Temperature (°C)", value=f"{avg_temp:.2f}")
st.metric(label="Minimum Temperature (°C)", value=f"{min_temp:.2f}")
with col_2:
st.metric(label="Maximum Temperature (°C)", value=f"{max_temp:.2f}")
st.metric(label="Std. Dev (°C)", value=f"{std_temp:.2f}")
# Column 2: Map
def temperature_to_color(temp, min_temp, max_temp):
"""Convert temperature to a color based on the gradient from blue (low) to red (high)."""
norm_temp = (temp - min_temp) / (max_temp - min_temp)
red = int(255 * norm_temp)
blue = int(255 * (1 - norm_temp))
return f'rgb({red}, 0, {blue})'
with col2:
# Create the base map
m = folium.Map(location=[22.3547, 114.1483], zoom_start=11, tiles='https://landsd.azure-api.net/dev/osm/xyz/basemap/gs/WGS84/tile/{z}/{x}/{y}.png?key=f4d3e21d4fc14954a1d5930d4dde3809',attr="Map infortmation from Lands Department")
folium.TileLayer(
tiles='https://mapapi.geodata.gov.hk/gs/api/v1.0.0/xyz/label/hk/en/wgs84/{z}/{x}/{y}.png',
attr="Map infortmation from Lands Department"
).add_to(m)
# Determine min and max temperatures for color scaling
min_temp = df['Temperature'].min()
max_temp = df['Temperature'].max()
# Create a color scale legend
colormap = folium.LinearColormap(
colors=['blue', 'white', 'red'],
index=[min_temp, (min_temp + max_temp) / 2, max_temp],
vmin=min_temp,
vmax=max_temp,
caption='Temperature (°C)'
)
colormap.add_to(m)
# Iterate through each row in the DataFrame
for _, row in df.iterrows():
lat = row['Latitude']
lon = row['Longitude']
station = row['Station']
temp = row['Temperature']
# Determine the color based on the temperature
color = temperature_to_color(temp, min_temp, max_temp) if pd.notna(temp) else 'gray'
# Create a marker with temperature data
folium.Marker(
location=[lat, lon],
popup=f"<p style='font-size: 12px; background-color: white; padding: 5px; border-radius: 5px;'>{station}: {temp:.1f}°C</p>",
icon=folium.DivIcon(
html=f'<div style="font-size: 10pt; color: {color}; padding: 2px; border-radius: 5px;">'
f'<strong>{temp:.1f}°C</strong></div>'
)
).add_to(m)
# Render the map in Streamlit
st_folium(m, use_container_width=True , height=650)
# Column 3: Data table
with col3:
# Set the table height using CSS
st.markdown(
"""
<style>
.dataframe-container {
height: 600px;
overflow-y: auto;
}
</style>
""",
unsafe_allow_html=True
)
# Display the DataFrame with the custom CSS class
st.dataframe(df[['Station', 'Temperature', 'Latitude', 'Longitude']], height=600)
# Add a refresh button
if st.button("Refresh Data"):
st.experimental_rerun()
# Automatically rerun every 5 minutes
if 'last_ran' not in st.session_state:
st.session_state.last_ran = datetime.now(timezone.utc)
current_time = datetime.now(timezone.utc)
if (current_time - st.session_state.last_ran).total_seconds() > 300:
st.session_state.last_ran = current_time
st.experimental_rerun() |