This notebook demonstrates how to retrieve and work with point forecast data from the Spire Weather API.
import os
import requests
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
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}Basic Point Forecast Request¶
# Request a forecast for New York City
params = {
'lat': 40.7128,
'lon': -74.0060,
'bundles': 'basic'
}
response = requests.get(
f'{BASE_URL}/forecast/point',
headers=HEADERS,
params=params
)
response.raise_for_status()
data = response.json()
print(f"Received {len(data['data'])} forecast times")Convert to DataFrame¶
def forecast_to_dataframe(response_data):
"""Convert API response to pandas DataFrame."""
records = []
for item in response_data['data']:
record = {
'valid_time': item['times']['valid_time'],
'issuance_time': item['times']['issuance_time'],
'lat': item['location']['coordinates']['lat'],
'lon': item['location']['coordinates']['lon'],
**item['values']
}
records.append(record)
df = pd.DataFrame(records)
df['valid_time'] = pd.to_datetime(df['valid_time'])
df['issuance_time'] = pd.to_datetime(df['issuance_time'])
return df
df = forecast_to_dataframe(data)
print(df.head())Available Variables¶
# List available variables
value_columns = [col for col in df.columns if col not in
['valid_time', 'issuance_time', 'lat', 'lon']]
print("Available forecast variables:")
for col in value_columns:
print(f" - {col}")Unit Conversion¶
The API returns SI units by default. Let’s add some helpful conversions:
# Temperature: Kelvin to Celsius
if 'air_temperature' in df.columns:
df['temperature_celsius'] = df['air_temperature'] - 273.15
df['temperature_fahrenheit'] = df['temperature_celsius'] * 9/5 + 32
# Wind: m/s to mph
if 'northward_wind' in df.columns and 'eastward_wind' in df.columns:
import numpy as np
df['wind_speed_ms'] = np.sqrt(df['northward_wind']**2 + df['eastward_wind']**2)
df['wind_speed_mph'] = df['wind_speed_ms'] * 2.237
df['wind_direction'] = (270 - np.degrees(np.arctan2(df['northward_wind'],
df['eastward_wind']))) % 360
print(df[['valid_time', 'temperature_celsius', 'temperature_fahrenheit',
'wind_speed_mph', 'wind_direction']].head())Plot Temperature Forecast¶
fig, ax = plt.subplots(figsize=(12, 5))
ax.plot(df['valid_time'], df['temperature_fahrenheit'], 'b-', linewidth=2)
ax.set_xlabel('Time')
ax.set_ylabel('Temperature (°F)')
ax.set_title('Temperature Forecast - New York City')
ax.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()Multi-Variable Plot¶
fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)
# Temperature
axes[0].plot(df['valid_time'], df['temperature_fahrenheit'], 'r-', linewidth=2)
axes[0].set_ylabel('Temperature (°F)')
axes[0].grid(True, alpha=0.3)
# Wind Speed
axes[1].plot(df['valid_time'], df['wind_speed_mph'], 'g-', linewidth=2)
axes[1].set_ylabel('Wind Speed (mph)')
axes[1].grid(True, alpha=0.3)
# Humidity
if 'relative_humidity' in df.columns:
axes[2].plot(df['valid_time'], df['relative_humidity'], 'b-', linewidth=2)
axes[2].set_ylabel('Relative Humidity (%)')
axes[2].set_ylim(0, 100)
axes[2].grid(True, alpha=0.3)
axes[2].set_xlabel('Time')
plt.suptitle('Weather Forecast - New York City', fontsize=14)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()Compare Multiple Locations¶
locations = {
'New York': (40.7128, -74.0060),
'Miami': (25.7617, -80.1918),
'Chicago': (41.8781, -87.6298),
'Denver': (39.7392, -104.9903)
}
location_data = {}
for city, (lat, lon) in locations.items():
params = {'lat': lat, 'lon': lon, 'bundles': 'basic'}
response = requests.get(f'{BASE_URL}/forecast/point', headers=HEADERS, params=params)
response.raise_for_status()
location_data[city] = forecast_to_dataframe(response.json())
# Add temperature conversions
if 'air_temperature' in location_data[city].columns:
location_data[city]['temperature_f'] = (location_data[city]['air_temperature'] - 273.15) * 9/5 + 32
print(f"Loaded data for {len(location_data)} cities")fig, ax = plt.subplots(figsize=(12, 6))
for city, city_df in location_data.items():
ax.plot(city_df['valid_time'], city_df['temperature_f'], label=city, linewidth=2)
ax.set_xlabel('Time')
ax.set_ylabel('Temperature (°F)')
ax.set_title('Temperature Forecast Comparison')
ax.legend()
ax.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()Using US Units Directly¶
# Request data in US customary units
params = {
'lat': 40.7128,
'lon': -74.0060,
'bundles': 'basic',
'unit_system': 'us'
}
response = requests.get(f'{BASE_URL}/forecast/point', headers=HEADERS, params=params)
response.raise_for_status()
us_data = response.json()
print(f"Unit system: {us_data['meta']['unit_system']}")
print(f"Sample temperature: {us_data['data'][0]['values'].get('air_temperature', 'N/A')}")Summary Statistics¶
# Calculate forecast statistics
print("New York Forecast Summary")
print("=" * 40)
nyc_df = location_data['New York']
print(f"Forecast period: {nyc_df['valid_time'].min()} to {nyc_df['valid_time'].max()}")
print()
print("Temperature (°F):")
print(f" Min: {nyc_df['temperature_f'].min():.1f}")
print(f" Max: {nyc_df['temperature_f'].max():.1f}")
print(f" Mean: {nyc_df['temperature_f'].mean():.1f}")Next Steps¶
File Downloads - Download GRIB2 forecast files
Route Forecasts - Weather along routes
Data Visualization - Advanced visualizations