Weather APIs Documentation¶
Overview¶
The drone operations management system integrates with three weather forecast APIs to provide reliable, aggregated weather data for flight planning. Weather forecasts are automatically fetched during project creation and cached for 24 hours to optimise API usage while ensuring data freshness.
Purpose in Drone Operations: - Pre-flight weather assessment for safety planning - Risk analysis and viability determination - Compliance with operational weather restrictions - Flight delay/cancellation decision support
Integration Architecture: - Aggregation Strategy: Data from all available sources is averaged for increased reliability - Graceful Degradation: System functions with 1-2 API failures - Caching: 24-hour cache stored in database (Project.weather_forecast JSON column) - Automatic Refetch: Cache validation on project view page
API 1: Open-Meteo (Primary Source)¶
Service Overview¶
- Provider: Open-Meteo.com
- Cost: Free, no API key required
- Forecast Range: 16 days
- Update Frequency: Hourly updates
- Rate Limits: 10,000 requests/day per IP
- Data Source: NOAA GFS, ECMWF models
Endpoint¶
GET https://api.open-meteo.com/v1/forecast
Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
| latitude | float | Yes | WGS84 decimal degrees |
| longitude | float | Yes | WGS84 decimal degrees |
| daily | string | Yes | Comma-separated list of daily variables |
| timezone | string | No | Auto-detect from coordinates (default: 'auto') |
| start_date | string | Yes | Flight date (YYYY-MM-DD) |
| end_date | string | Yes | Same as start_date for single-day forecast |
Daily Variables Used:
- temperature_2m_max - Maximum temperature at 2m (°C)
- temperature_2m_min - Minimum temperature at 2m (°C)
- temperature_2m_mean - Mean temperature at 2m (°C)
- precipitation_sum - Total precipitation (mm)
- precipitation_probability_max - Maximum precipitation probability (%)
- wind_speed_10m_max - Maximum wind speed at 10m (km/h)
- wind_gusts_10m_max - Maximum wind gusts at 10m (km/h)
- weather_code - WMO weather code (0-99)
Request Example¶
import requests
params = {
'latitude': 55.9533,
'longitude': -3.1883,
'daily': 'temperature_2m_max,temperature_2m_min,temperature_2m_mean,precipitation_sum,precipitation_probability_max,wind_speed_10m_max,wind_gusts_10m_max,weather_code',
'timezone': 'auto',
'start_date': '2025-01-15',
'end_date': '2025-01-15'
}
response = requests.get('https://api.open-meteo.com/v1/forecast', params=params, timeout=10)
data = response.json()
Response Format¶
{
"latitude": 55.95,
"longitude": -3.19,
"timezone": "Europe/London",
"daily": {
"time": ["2025-01-15"],
"temperature_2m_max": [12.5],
"temperature_2m_min": [8.0],
"temperature_2m_mean": [10.2],
"precipitation_sum": [2.3],
"precipitation_probability_max": [45],
"wind_speed_10m_max": [18.5],
"wind_gusts_10m_max": [32.0],
"weather_code": [61]
}
}
WMO Weather Code Mapping¶
| Code Range | Condition |
|---|---|
| 0 | Clear |
| 1-3 | Partly Cloudy |
| 45, 48 | Foggy |
| 51-57 | Drizzle |
| 61-67, 80-82 | Rain |
| 71-77, 85-86 | Snow |
| 95-99 | Thunderstorm |
Error Handling¶
- Timeout: 10-second timeout, returns None on failure
- HTTP Errors: Logged, returns None
- Parsing Errors: Invalid/missing fields logged, returns None
- Rate Limit: 429 response - exponential backoff (not implemented, relies on cache)
API 2: OpenWeatherMap¶
Service Overview¶
- Provider: OpenWeatherMap.org
- Cost: Free tier available (requires API key)
- Forecast Range: 5 days (free tier)
- Update Frequency: Every 3 hours
- Rate Limits: 60 calls/min, 1,000,000 calls/month (free tier)
- Data Source: Multiple numerical weather models
Endpoint¶
GET https://api.openweathermap.org/data/2.5/forecast
Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
| lat | float | Yes | WGS84 decimal degrees |
| lon | float | Yes | WGS84 decimal degrees |
| appid | string | Yes | API key from account dashboard |
| units | string | Yes | Use 'metric' for Celsius/km/h |
API Key Setup¶
- Create free account at https://openweathermap.org/api
- Navigate to API keys section in dashboard
- Generate new API key (activation takes ~2 hours)
- Add to
.envfile:OPENWEATHERMAP_API_KEY=your_api_key_here
Request Example¶
import requests
import os
params = {
'lat': 55.9533,
'lon': -3.1883,
'appid': os.getenv('OPENWEATHERMAP_API_KEY'),
'units': 'metric'
}
response = requests.get('https://api.openweathermap.org/data/2.5/forecast', params=params, timeout=10)
data = response.json()
Response Format¶
{
"list": [
{
"dt": 1705330800,
"main": {
"temp": 10.5,
"temp_min": 8.2,
"temp_max": 12.1
},
"weather": [
{
"main": "Rain",
"description": "light rain"
}
],
"wind": {
"speed": 5.14,
"gust": 8.23
},
"pop": 0.45,
"rain": {
"3h": 0.76
}
}
// ... more 3-hour forecasts
]
}
Data Aggregation (3-hour to Daily)¶
The implementation aggregates 3-hour forecasts for the target date: - Temperature Avg: Mean of all 3-hour temps - Temperature Min/Max: Min/max across all 3-hour periods - Precipitation: Sum of all 3-hour rainfall amounts - Precipitation Probability: Maximum probability (conservative) - Wind Speed: Mean of all 3-hour speeds - Wind Gusts: Maximum gust across all periods - Conditions: Most common weather.main value
Error Handling¶
- 401 Unauthorized: Invalid API key - skip this source
- Timeout: 10-second timeout, returns None
- No Data for Date: If date > 5 days away, returns None
- Rate Limit: 429 response - skip this source (relies on cache)
API 3: Met Office DataPoint (UK Only)¶
Service Overview¶
- Provider: UK Met Office
- Cost: Free tier available (requires API key)
- Forecast Range: 5 days
- Coverage: UK locations only (49.9-60.9°N, -8.2-1.8°E)
- Rate Limits: 5,000 requests/day
- Data Source: UK Met Office numerical models
Geographic Coverage¶
The implementation automatically checks if coordinates are within UK bounds:
UK_BOUNDS = {
'lat_min': 49.9, # Southern UK (Channel Islands)
'lat_max': 60.9, # Northern UK (Shetland)
'lon_min': -8.2, # Western UK (Ireland)
'lon_max': 1.8 # Eastern UK
}
Behaviour: - If location outside UK → Met Office API is skipped (not an error) - This prevents wasted API calls for non-UK operations
API Key Setup¶
- Register at https://www.metoffice.gov.uk/services/data/datapoint
- Subscribe to "Site Specific Forecast" API
- Copy JWT token from API dashboard
- Add to
.envfile:MET_OFFICE_API_KEY=your_jwt_token_here
Implementation Status¶
✅ Fully implemented
The Met Office integration is complete and operational:
- JWT authentication via apikey header
- UK location bounds checking (auto-skips non-UK locations)
- 3-hourly forecast aggregation into daily summaries
- Weather code mapping (codes 0-30) to human-readable conditions
- Automatic unit conversions (m/s to km/h for wind speeds)
Endpoint¶
GET https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/daily
Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
| latitude | float | Yes | WGS84 decimal degrees |
| longitude | float | Yes | WGS84 decimal degrees |
| excludeParameterMetadata | boolean | No | Set to 'false' to include parameter metadata |
| includeLocationName | boolean | No | Set to 'true' to include location name in response |
Authentication¶
- Method: API key header
- Header:
apikey: {your_jwt_token} - Token Type: JWT (long-lived bearer token)
Request Example¶
import requests
import os
params = {
'latitude': 55.9533,
'longitude': -3.1883,
'excludeParameterMetadata': 'false',
'includeLocationName': 'true'
}
headers = {
'apikey': os.getenv('MET_OFFICE_API_KEY'),
'accept': 'application/json'
}
response = requests.get(
'https://data.hub.api.metoffice.gov.uk/sitespecific/v0/point/daily',
params=params,
headers=headers,
timeout=10
)
data = response.json()
Response Format¶
Returns GeoJSON FeatureCollection with 3-hourly forecasts:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-3.1883, 55.9533]
},
"properties": {
"location": {
"name": "Edinburgh"
},
"timeSeries": [
{
"time": "2025-01-15T00:00Z",
"maxScreenAirTemp": 12.5,
"minScreenAirTemp": 8.0,
"totalPrecipAmount": 0.5,
"probOfPrecipitation": 45,
"windSpeed10m": 5.1,
"max10mWindGust": 8.9,
"significantWeatherCode": 7
}
// ... more 3-hourly entries
]
}
}
]
}
Weather Code Mapping¶
| Code | Condition | Mapped To |
|---|---|---|
| 0 | Clear night | Clear |
| 1 | Sunny day | Clear |
| 2, 3 | Partly cloudy (night/day) | Partly Cloudy |
| 5, 6 | Mist/Fog | Foggy |
| 7, 8 | Cloudy/Overcast | Cloudy |
| 11 | Drizzle | Drizzle |
| 9-10, 12-15 | Rain showers/Light rain/Heavy rain | Rain |
| 16-21 | Sleet/Hail (showers) | Sleet/Hail |
| 22-27 | Snow (light/heavy showers) | Snow |
| 28-30 | Thunder (showers) | Thunderstorm |
Full code definitions: https://www.metoffice.gov.uk/services/data/datapoint/code-definitions
Data Aggregation (3-hour to Daily)¶
The implementation aggregates 3-hourly forecasts for the target date: - Temperature Avg: Mean of all max and min temps - Temperature Min/Max: Min/max across all periods - Precipitation: Sum of all 3-hour amounts (not average) - Precipitation Probability: Maximum probability (conservative) - Wind Speed: Mean of all 3-hour speeds (converted m/s → km/h) - Wind Gusts: Maximum gust across all periods (converted m/s → km/h) - Conditions: Most common weather code (majority vote)
Implementation Notes¶
Python Integration Example¶
from app.utils.weather_api import WeatherAPI
# Fetch forecast for Edinburgh
latitude = 55.9533
longitude = -3.1883
flight_date = '2025-01-15'
result = WeatherAPI.get_forecast(latitude, longitude, flight_date)
if result['success']:
forecast = result['forecast_data']
metadata = result['metadata']
print(f"Temperature: {forecast['temperature_avg_c']}°C")
print(f"Wind: {forecast['wind_speed_kmh']} km/h")
print(f"Precipitation: {forecast['precipitation_mm']} mm")
print(f"Sources: {metadata['sources_used']}")
else:
print(f"Error: {result['error']}")
Coordinate Format¶
All APIs use WGS84 decimal degrees (GPS standard): - Latitude: -90 to 90 (positive = North) - Longitude: -180 to 180 (positive = East) - Example: Edinburgh = (55.9533, -3.1883)
Date Format¶
- Input: ISO 8601 date string (
YYYY-MM-DD) - Validation: 0-16 days from current date
- Timezone: All APIs return data in local timezone
Unit Conversions¶
The implementation handles automatic unit conversions:
| Measurement | API Units | Stored Units | Conversion |
|---|---|---|---|
| Temperature | °C | °C | No conversion |
| Wind Speed | m/s (OWM) | km/h | × 3.6 |
| Wind Speed | km/h (Open-Meteo) | km/h | No conversion |
| Precipitation | mm | mm | No conversion |
| Precipitation Probability | 0-1 (OWM) | 0-100 | × 100 |
| Precipitation Probability | 0-100 (Open-Meteo) | 0-100 | No conversion |
Timeout Configuration¶
- Default: 10 seconds per API call
- Configurable: Set
WeatherAPI.API_TIMEOUT(seconds) - Parallel Execution: All 3 APIs called simultaneously (not sequential)
- Total Max Time: ~10 seconds (parallel execution)
Error Handling Strategy¶
try:
weather_data = WeatherAPI.get_forecast(lat, lon, date)
if weather_data['success']:
# Use forecast data
forecast = weather_data['forecast_data']
sources_used = weather_data['metadata']['sources_used']
# Check if we got data from multiple sources
if len(sources_used) >= 2:
print("High confidence (multiple sources)")
elif len(sources_used) == 1:
print("Moderate confidence (single source)")
else:
# All APIs failed
print(f"Weather unavailable: {weather_data['error']}")
except Exception as e:
# Unexpected error (should never happen in production)
logger.error(f"Weather API error: {e}")
References¶
Official Documentation¶
- Open-Meteo: https://open-meteo.com/en/docs
- OpenWeatherMap: https://openweathermap.org/forecast5
- Met Office: https://www.metoffice.gov.uk/services/data/datapoint
Related Documentation¶
- Weather Forecast Design - Architecture and aggregation strategy
- Project Model - Database schema including weather_forecast column
External Resources¶
- WMO Weather Codes: https://www.nodc.noaa.gov/archive/arc0021/0002199/1.1/data/0-data/HTML/WMO-CODE/WMO4677.HTM
- WGS84 Coordinate System: https://en.wikipedia.org/wiki/World_Geodetic_System