Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import requests | |
| import folium | |
| from streamlit_folium import st_folium | |
| import pandas as pd | |
| import plotly.graph_objs as go | |
| import branca.colormap as cm | |
| import pytz | |
| from datetime import datetime | |
| # Set page layout to wide | |
| st.set_page_config(layout="wide", page_title="Real-Time Wind Data Dashboard") | |
| def fetch_geojson_data(url): | |
| response = requests.get(url) | |
| data = response.json() | |
| hk_tz = pytz.timezone('Asia/Hong_Kong') | |
| fetch_time = datetime.now(hk_tz).strftime('%Y-%m-%dT%H:%M:%S') | |
| return data, fetch_time | |
| # Function to calculate wind statistics | |
| def calculate_wind_stats(features): | |
| gust_speeds = [feature['properties']['10-Minute Maximum Gust(km/hour)'] for feature in features if | |
| feature['properties']['10-Minute Maximum Gust(km/hour)'] is not None] | |
| mean_speeds = [feature['properties']['10-Minute Mean Speed(km/hour)'] for feature in features if | |
| feature['properties']['10-Minute Mean Speed(km/hour)'] is not None] | |
| if not gust_speeds: | |
| return None, None, None, None | |
| avg_gust = sum(gust_speeds) / len(gust_speeds) | |
| min_gust = min(gust_speeds) | |
| max_gust = max(gust_speeds) | |
| avg_mean_speed = sum(mean_speeds) / len(mean_speeds) if mean_speeds else None | |
| return avg_gust, min_gust, max_gust, avg_mean_speed | |
| # Function to convert wind direction to degrees | |
| def mean_wind_direction_to_degrees(direction): | |
| directions = { | |
| 'North': 0, 'Northeast': 45, 'East': 90, 'Southeast': 135, | |
| 'South': 180, 'Southwest': 225, 'West': 270, 'Northwest': 315 | |
| } | |
| return directions.get(direction, 0) | |
| # Fetch GeoJSON data | |
| url = 'https://csdi.vercel.app/weather/wind' | |
| geo_data, fetch_time = fetch_geojson_data(url) | |
| # Calculate wind statistics | |
| avg_gust, min_gust, max_gust, avg_mean_speed = calculate_wind_stats(geo_data['features']) | |
| # Create a map centered on a specific location | |
| map_center = [22.35473034278638, 114.14827142452518] # Coordinates of Hong Kong | |
| my_map = folium.Map(location=map_center, zoom_start=10.65, 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(my_map) | |
| # Create a colormap for wind speed with limited width | |
| colormap = cm.LinearColormap(colors=['#000000', '#0066eb', '#ff3d77', '#eb0000'], | |
| vmin=0, vmax=85) | |
| my_map.add_child(colormap) | |
| # Function to calculate arrow size based on wind speed | |
| def get_arrow_size(speed): | |
| if speed is None: | |
| return 20 | |
| return max(20, min(50, speed * 2)) | |
| # Add the GeoJSON data to the map with arrow markers | |
| for feature in geo_data['features']: | |
| coordinates = feature['geometry']['coordinates'] | |
| mean_wind_direction = feature['properties']['10-Minute Mean Wind Direction(Compass points)'] | |
| mean_speed = feature['properties']['10-Minute Mean Speed(km/hour)'] | |
| # Skip plotting if wind direction is null | |
| if mean_wind_direction is None: | |
| continue | |
| # Calculate rotation angle for wind direction | |
| rotation_angle = mean_wind_direction_to_degrees(mean_wind_direction) | |
| # Calculate arrow size based on wind speed | |
| arrow_size = get_arrow_size(mean_speed) | |
| # Determine color based on wind speed | |
| color = colormap(mean_speed) if mean_speed is not None else 'gray' | |
| # Create an arrow marker for wind direction | |
| folium.Marker( | |
| location=[coordinates[1], coordinates[0]], | |
| icon=folium.DivIcon(html=f""" | |
| <div style=" | |
| width: {arrow_size}px; height: {arrow_size}px; | |
| display: flex; align-items: center; justify-content: center; | |
| transform: rotate({rotation_angle}deg); | |
| "> | |
| <svg width="{arrow_size}" height="{arrow_size}" viewBox="0 0 24 24" fill="none" stroke="{color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <line x1="12" y1="5" x2="12" y2="19"></line> | |
| <polyline points="5 12 12 5 19 12"></polyline> | |
| </svg> | |
| </div> | |
| """), | |
| popup=folium.Popup(f""" | |
| <b>{feature['properties']['Automatic Weather Station']}</b><br> | |
| Direction: {mean_wind_direction}<br> | |
| Speed: {mean_speed} km/h<br> | |
| Max Gust: {feature['properties']['10-Minute Maximum Gust(km/hour)']} km/h | |
| """, max_width=300) | |
| ).add_to(my_map) | |
| col1, col2, col3 = st.columns([1.65, 2, 1.15]) | |
| with col1: | |
| if geo_data['features']: | |
| wind_directions = [feature['properties']['10-Minute Mean Wind Direction(Compass points)'] for feature in | |
| geo_data['features']] | |
| direction_counts = {d: wind_directions.count(d) for d in | |
| ['North', 'Northeast', 'East', 'Southeast', 'South', 'Southwest', 'West', 'Northwest']} | |
| # Prepare wind speeds for each direction | |
| direction_speeds = {d: [] for d in | |
| ['North', 'Northeast', 'East', 'Southeast', 'South', 'Southwest', 'West', 'Northwest']} | |
| for feature in geo_data['features']: | |
| direction = feature['properties']['10-Minute Mean Wind Direction(Compass points)'] | |
| speed = feature['properties']['10-Minute Mean Speed(km/hour)'] | |
| if direction in direction_speeds and speed is not None: | |
| direction_speeds[direction].append(speed) | |
| # Calculate average wind speed for each direction | |
| average_speeds = {d: sum(speeds) / len(speeds) if speeds else 0 for d, speeds in direction_speeds.items()} | |
| # Plot wind direction rose with average wind speed | |
| fig = go.Figure() | |
| # Add polar bar for wind direction | |
| fig.add_trace(go.Barpolar( | |
| r=[direction_counts[d] for d in direction_counts.keys()], | |
| theta=list(direction_counts.keys()), | |
| name='Wind Direction Count', | |
| marker_color='#0008ff', | |
| opacity=0.5 | |
| )) | |
| # Add radial bar for average wind speed | |
| fig.add_trace(go.Barpolar( | |
| r=list(average_speeds.values()), | |
| theta=list(average_speeds.keys()), | |
| name='Average Wind Speed', | |
| marker_color='#ff0019', # Orange color for wind speed | |
| opacity=0.5, | |
| thetaunit='radians', # Ensures radial bars are correctly positioned | |
| base=0 # Base of the radial bars starts from 0 | |
| )) | |
| fig.update_layout( | |
| polar=dict( | |
| radialaxis=dict( | |
| visible=False, | |
| range=[0, max(direction_counts.values())] | |
| ), | |
| angularaxis=dict( | |
| tickvals=list(direction_counts.keys()), | |
| ticktext=['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'], | |
| rotation=90, # Rotate to make North the top | |
| direction='clockwise' | |
| ) | |
| ), | |
| width=500, | |
| height=380, | |
| title={'text': 'Wind Direction and Average Speed Rose Plot', 'font': {'size': 18}}, | |
| legend={'x': 0.8, 'y': 0.95} | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.caption(f"Data fetched at: {fetch_time}") | |
| if avg_gust is not None: | |
| col1a, col1b = st.columns(2) | |
| with col1a: | |
| st.metric(label="Avg Max Gust (km/h)", value=f"{avg_gust:.2f}") | |
| st.metric(label="Min Max Gust (km/h)", value=f"{min_gust}") | |
| with col1b: | |
| st.metric(label="Max Max Gust (km/h)", value=f"{max_gust}") | |
| if avg_mean_speed is not None: | |
| st.metric(label="Avg Mean Speed (km/h)", value=f"{avg_mean_speed:.2f}") | |
| else: | |
| st.write("No valid wind data available to calculate statistics.") | |
| gust_speeds = [feature['properties']['10-Minute Maximum Gust(km/hour)'] for feature in geo_data['features'] if | |
| feature['properties']['10-Minute Maximum Gust(km/hour)'] is not None] | |
| with col3: | |
| table_data = [{ | |
| 'Weather Station': feature['properties']['Automatic Weather Station'], | |
| 'Mean Wind Direction': feature['properties']['10-Minute Mean Wind Direction(Compass points)'], | |
| 'Mean Speed(km/hour)': feature['properties']['10-Minute Mean Speed(km/hour)'], | |
| 'Maximum Gust(km/hour)': feature['properties']['10-Minute Maximum Gust(km/hour)'] | |
| } for feature in geo_data['features']] | |
| st.dataframe(pd.DataFrame(table_data), height=600) | |
| with col2: | |
| # Display map | |
| st_folium(my_map, use_container_width=True , height=650) |