This notebook demonstrates visualization techniques for Spire Weather API data.
import os
import requests
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime, timedelta
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.environ.get('SPIRE_API_KEY')
BASE_URL = 'https://api.wx.spire.com'
HEADERS = {'spire-api-key': API_KEY}
# Set matplotlib style
plt.style.use('seaborn-v0_8-whitegrid')Helper Functions¶
def get_point_forecast(lat, lon, bundles='basic'):
"""Get point forecast and return as DataFrame."""
response = requests.get(
f'{BASE_URL}/forecast/point',
headers=HEADERS,
params={'lat': lat, 'lon': lon, 'bundles': bundles}
)
response.raise_for_status()
data = response.json()
records = []
for item in data['data']:
record = {
'valid_time': item['times']['valid_time'],
**item['values']
}
records.append(record)
df = pd.DataFrame(records)
df['valid_time'] = pd.to_datetime(df['valid_time'])
return df
def kelvin_to_fahrenheit(k):
"""Convert Kelvin to Fahrenheit."""
return (k - 273.15) * 9/5 + 32Time Series Plot¶
# Get forecast for Denver
df = get_point_forecast(39.7392, -104.9903)
df['temp_f'] = kelvin_to_fahrenheit(df['air_temperature'])
fig, ax = plt.subplots(figsize=(14, 5))
ax.plot(df['valid_time'], df['temp_f'], color='#E74C3C', linewidth=2)
ax.fill_between(df['valid_time'], df['temp_f'], alpha=0.3, color='#E74C3C')
ax.set_xlabel('Date/Time', fontsize=12)
ax.set_ylabel('Temperature (°F)', fontsize=12)
ax.set_title('Temperature Forecast - Denver, CO', fontsize=14, fontweight='bold')
# Format x-axis dates
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %d\n%H:%M'))
ax.xaxis.set_major_locator(mdates.HourLocator(interval=12))
plt.tight_layout()
plt.show()Multi-Variable Dashboard¶
# Calculate wind speed
if 'northward_wind' in df.columns and 'eastward_wind' in df.columns:
df['wind_speed_mph'] = np.sqrt(df['northward_wind']**2 + df['eastward_wind']**2) * 2.237
fig, axes = plt.subplots(4, 1, figsize=(14, 12), sharex=True)
# Temperature
axes[0].plot(df['valid_time'], df['temp_f'], color='#E74C3C', linewidth=2)
axes[0].set_ylabel('Temp (°F)', fontsize=11)
axes[0].axhline(y=32, color='blue', linestyle='--', alpha=0.5, label='Freezing')
axes[0].legend(loc='upper right')
# Wind Speed
if 'wind_speed_mph' in df.columns:
axes[1].plot(df['valid_time'], df['wind_speed_mph'], color='#3498DB', linewidth=2)
axes[1].fill_between(df['valid_time'], df['wind_speed_mph'], alpha=0.3, color='#3498DB')
axes[1].set_ylabel('Wind (mph)', fontsize=11)
# Humidity
if 'relative_humidity' in df.columns:
axes[2].plot(df['valid_time'], df['relative_humidity'], color='#27AE60', linewidth=2)
axes[2].fill_between(df['valid_time'], df['relative_humidity'], alpha=0.3, color='#27AE60')
axes[2].set_ylabel('Humidity (%)', fontsize=11)
axes[2].set_ylim(0, 100)
# Precipitation
if 'precipitation_amount' in df.columns:
axes[3].bar(df['valid_time'], df['precipitation_amount'],
width=0.2, color='#9B59B6', alpha=0.7)
axes[3].set_ylabel('Precip (mm)', fontsize=11)
else:
axes[3].text(0.5, 0.5, 'Precipitation data not available',
ha='center', va='center', transform=axes[3].transAxes)
axes[3].set_xlabel('Date/Time', fontsize=12)
axes[3].xaxis.set_major_formatter(mdates.DateFormatter('%b %d\n%H:%M'))
fig.suptitle('Weather Forecast Dashboard - Denver, CO', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()City Comparison Heatmap¶
cities = {
'Seattle': (47.6062, -122.3321),
'San Francisco': (37.7749, -122.4194),
'Phoenix': (33.4484, -112.0740),
'Denver': (39.7392, -104.9903),
'Dallas': (32.7767, -96.7970),
'Chicago': (41.8781, -87.6298),
'Miami': (25.7617, -80.1918),
'New York': (40.7128, -74.0060),
}
# Get forecasts for all cities
city_data = {}
for city, (lat, lon) in cities.items():
try:
city_df = get_point_forecast(lat, lon)
city_df['temp_f'] = kelvin_to_fahrenheit(city_df['air_temperature'])
city_data[city] = city_df
except Exception as e:
print(f"Error fetching {city}: {e}")
print(f"Retrieved data for {len(city_data)} cities")# Create temperature matrix
if city_data:
# Get common time points (first 24 hours)
sample_df = list(city_data.values())[0]
time_points = sample_df['valid_time'][:24]
temp_matrix = []
city_names = []
for city, df in city_data.items():
temps = df['temp_f'][:24].values
if len(temps) == 24:
temp_matrix.append(temps)
city_names.append(city)
temp_matrix = np.array(temp_matrix)
# Create heatmap
fig, ax = plt.subplots(figsize=(16, 8))
im = ax.imshow(temp_matrix, aspect='auto', cmap='RdYlBu_r')
# Labels
ax.set_yticks(range(len(city_names)))
ax.set_yticklabels(city_names)
ax.set_xlabel('Forecast Hour', fontsize=12)
ax.set_title('24-Hour Temperature Forecast Comparison (°F)', fontsize=14, fontweight='bold')
# Colorbar
cbar = plt.colorbar(im, ax=ax)
cbar.set_label('Temperature (°F)', fontsize=11)
plt.tight_layout()
plt.show()Wind Rose Diagram¶
# Calculate wind direction for Denver
df_denver = city_data.get('Denver')
if df_denver is not None and 'northward_wind' in df_denver.columns:
df_denver['wind_dir'] = (270 - np.degrees(
np.arctan2(df_denver['northward_wind'], df_denver['eastward_wind'])
)) % 360
df_denver['wind_speed'] = np.sqrt(
df_denver['northward_wind']**2 + df_denver['eastward_wind']**2
) * 2.237 # m/s to mph
# Create polar plot
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(projection='polar'))
# Convert to radians and rotate so 0 is North
theta = np.radians(90 - df_denver['wind_dir']) # Convert to standard polar
r = df_denver['wind_speed']
# Scatter plot of wind vectors
scatter = ax.scatter(theta, r, c=df_denver.index, cmap='viridis',
alpha=0.6, s=50)
ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
ax.set_title('Wind Direction and Speed - Denver\n(Color = Time)',
fontsize=12, fontweight='bold', pad=20)
# Add colorbar
cbar = plt.colorbar(scatter, ax=ax, pad=0.1)
cbar.set_label('Forecast Hour')
plt.show()
else:
print("Wind data not available")Diurnal Cycle Analysis¶
if city_data:
fig, ax = plt.subplots(figsize=(10, 6))
colors = plt.cm.tab10(np.linspace(0, 1, len(city_data)))
for (city, df), color in zip(city_data.items(), colors):
# Group by hour of day
df_copy = df.copy()
df_copy['hour'] = df_copy['valid_time'].dt.hour
hourly_mean = df_copy.groupby('hour')['temp_f'].mean()
ax.plot(hourly_mean.index, hourly_mean.values,
label=city, linewidth=2, color=color)
ax.set_xlabel('Hour of Day (UTC)', fontsize=12)
ax.set_ylabel('Temperature (°F)', fontsize=12)
ax.set_title('Average Diurnal Temperature Cycle by City', fontsize=14, fontweight='bold')
ax.legend(loc='upper left', bbox_to_anchor=(1.02, 1))
ax.set_xticks(range(0, 24, 3))
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()GRIB2 Visualization¶
If you have downloaded GRIB2 files:
try:
import xarray as xr
import cartopy.crs as ccrs
import cartopy.feature as cfeature
# Check for downloaded GRIB2 files
data_dir = 'downloaded_data'
if os.path.exists(data_dir):
grib_files = [f for f in os.listdir(data_dir) if f.endswith('.grib2')]
if grib_files:
filepath = os.path.join(data_dir, grib_files[0])
print(f"Loading: {filepath}")
ds = xr.open_dataset(filepath, engine='cfgrib')
print("Dataset variables:", list(ds.data_vars))
# Plot first available variable
var_name = list(ds.data_vars)[0]
data = ds[var_name]
fig, ax = plt.subplots(
figsize=(14, 8),
subplot_kw={'projection': ccrs.PlateCarree()}
)
data.plot(ax=ax, transform=ccrs.PlateCarree(),
cmap='viridis', add_colorbar=True)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS, linestyle=':')
ax.set_title(f'{var_name}', fontsize=14)
plt.show()
else:
print("No GRIB2 files found. Run the file download notebook first.")
else:
print("No downloaded_data directory. Run the file download notebook first.")
except ImportError as e:
print(f"Required library not installed: {e}")
print("Install with: conda install -c conda-forge xarray cfgrib cartopy")Summary¶
This notebook demonstrated:
Time series plots - Temperature forecasts over time
Multi-variable dashboards - Multiple weather parameters
City comparison heatmaps - Compare forecasts across locations
Wind rose diagrams - Wind direction and speed visualization
Diurnal cycle analysis - Daily temperature patterns
GRIB2 map visualization - Gridded data on maps