Wind Data Examples

These examples demonstrate how to retrieve and process wind speed data from Jua using two different access methods: the REST API for point forecasts and direct Zarr file access for gridded data.

Before running these scripts, ensure you have:

  1. Set up your API credentials by following the instructions in our Getting Started guide. The scripts expect your Key ID and Secret in environment variables.

  2. Installed the necessary Python packages. See the list below for each example. You can find more details on recommended packages here.

Wind Data via REST API

This example shows how to fetch 100m wind speed forecasts for multiple specific locations (German cities) using the Jua REST API. This method is ideal for querying a limited number of points.

Dependencies

This script requires the following Python packages:

pandas
requests

Setting up Authentication

The script requires your Jua API Key ID and Secret to be available as environment variables for constructing the X-API-Key header.

import os

# Retrieve credentials from environment variables
API_KEY_ID = os.environ["JUA_API_KEY_ID"]
API_SECRET = os.environ["JUA_API_SECRET"]

# Create the authentication header required by the REST API
AUTH_HEADER = {"X-API-Key": f"{API_KEY_ID}:{API_SECRET}"}

# NB: It may take up to 5 minutes for new API keys to be deployed.
# In the meantime, you may get unauthorized errors (HTTP 401).

Script

This script defines locations, sends a POST request to the API, and processes the JSON response into a Pandas DataFrame.

import os
import requests
import pandas as pd
from datetime import datetime, timezone, timedelta

# --- Authentication Setup ---
API_KEY_ID = os.environ["JUA_API_KEY_ID"]
API_SECRET = os.environ["JUA_API_SECRET"]
AUTH_HEADER = {"X-API-Key": f"{API_KEY_ID}:{API_SECRET}"}

# --- Configuration ---
# Define locations of interest (City Name: (Latitude, Longitude))
locations = {
    "Hamburg": (53.5488, 9.9872),
    "Berlin": (52.52, 13.40),
    "Munich": (48.1351, 11.5820),
    "Frankfurt": (50.1109, 8.6821),
}

# API endpoint for the latest EPT 1.5km forecast
api_url = "https://api.jua.ai/v1/forecasting/ept1_5/forecasts/latest"

# Request parameters
request_payload = {
    "min_lead_time": 0,       # Start from the initialization time
    "max_lead_time": 12,      # Get forecasts up to 12 hours ahead
    "points": [
        {"lat": lat, "lon": lon, "name": name} 
        for name, (lat, lon) in locations.items()
    ],
    "variables": ["wind_speed_100m"], # Request 100m wind speed
}

# --- API Request ---
print(f"Requesting data from {api_url}...")
response = requests.post(
    api_url,
    json=request_payload,
    headers=AUTH_HEADER,
)
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
data = response.json()
print("Data received successfully.")

# --- Data Processing ---
# Extract forecast initialization time and convert to datetime object
init_time = datetime.fromisoformat(data["init_time"].replace("Z", "+00:00"))

# Process the results into a more usable format (list of dictionaries)
processed_data = []
for point_data in data["points"]:
    point_name = point_data.get("name", f"{point_data['returned_latlon'][0]},{point_data['returned_latlon'][1]}")
    returned_lat, returned_lon = point_data["returned_latlon"]
    wind_speeds = point_data["variables"]["wind_speed_100m"]["values"]
    
    for i, speed in enumerate(wind_speeds):
        # Calculate the valid time for this forecast step
        # Assuming lead times are hourly starting from init_time + min_lead_time
        # Check API documentation for exact time handling if min_lead_time changes
        lead_hour = request_payload["min_lead_time"] + i
        valid_time = init_time + timedelta(hours=lead_hour)
        
        processed_data.append(
            {
                "Location": point_name,
                "Latitude": returned_lat,
                "Longitude": returned_lon,
                "ValidTime_UTC": valid_time,
                "LeadTime_Hours": lead_hour,
                "WindSpeed_100m_mps": speed, # m/s is the standard unit
            }
        )

# Convert the processed data into a Pandas DataFrame
wind_speeds_df = pd.DataFrame(processed_data)

# --- Output ---
print(f"\nWind speed forecast (Init: {init_time.strftime('%Y-%m-%d %H:%M UTC')}):")
print(wind_speeds_df)

Key Concepts Demonstrated

  • Authenticating REST API requests using an X-API-Key header.

  • Constructing a POST request payload to specify multiple locations, variables, and time ranges.

  • Handling potential API errors using response.raise_for_status().

  • Parsing the JSON response structure, including initialization time and point-specific data.

  • Calculating valid forecast timestamps based on initialization time and lead hours.

  • Structuring the extracted data into a Pandas DataFrame for analysis.


Wind Data via Zarr Files

This alternative approach shows how to retrieve 100m wind speed data (100si) for the same locations using direct access to Zarr files via xarray. This method is better suited for accessing larger amounts of gridded data or specific historical forecasts.

Dependencies

This script requires the following Python packages:

aiohttp
xarray >= 2023.6.0 # Ensure version supports decode_timedelta=False with zarr
zarr
pandas # Used only for displaying the final DataFrame

Setting up Authentication

The script requires your Jua API Key ID and Secret to be available as environment variables for aiohttp.BasicAuth used by xarray's storage options.

import os
from aiohttp import BasicAuth

# Retrieve credentials from environment variables
API_KEY_ID = os.environ["JUA_API_KEY_ID"]
API_SECRET = os.environ["JUA_API_SECRET"]

# Create authentication object for xarray's storage_options
AUTH = BasicAuth(login=API_KEY_ID, password=API_SECRET)

# NB: It may take up to 5 minutes for new API keys to be deployed.
# In the meantime, you may get unauthorized errors.

Script

This script constructs URLs for multiple Zarr files corresponding to different lead times for a specific forecast initialization, opens them as a single dataset using xr.open_mfdataset, selects the desired variable and locations, and computes the result.

import xarray as xr
from aiohttp import BasicAuth
import os
import time
import pandas as pd # Only for final display

# --- Authentication Setup ---
API_KEY_ID = os.environ["JUA_API_KEY_ID"]
API_SECRET = os.environ["JUA_API_SECRET"]
AUTH = BasicAuth(login=API_KEY_ID, password=API_SECRET)

# --- Configuration ---
# Use today's 00Z initialization time (adjust if needed)
init_time_str = time.strftime("%Y%m%d00")

# Define lead times: e.g., every 6 hours for the first 24 hours
lead_times = list(range(0, 25, 6))

# Define locations of interest (Latitude, Longitude)
# Note: Order matters for direct list selection in .sel
lats = [53.5488, 52.5200, 48.1351, 50.1109] # Hamburg, Berlin, Munich, Frankfurt
longs = [9.9872, 13.4050, 11.5820, 8.6821]
location_names = ["Hamburg", "Berlin", "Munich", "Frankfurt"]

# --- Data Loading ---
# Construct URLs for each forecast lead time Zarr file
forecast_urls = [
    f"https://data.jua.ai/forecasts/ept-1.5/{init_time_str}/{lead_time}.zarr"
    for lead_time in lead_times
]

print(f"Opening {len(forecast_urls)} Zarr datasets for init {init_time_str}...")
# Open multiple Zarr datasets efficiently as a single xarray Dataset
ds = xr.open_mfdataset(
    forecast_urls,
    parallel=True,       # Use dask for parallel loading if available
    engine="zarr",
    consolidated=True,   # Use consolidated metadata if available
    decode_timedelta=False, # Keep prediction_timedelta as numeric hours
    storage_options={"auth": AUTH}, # Pass authentication
)
print("Datasets opened.")

# --- Data Selection & Processing ---
# Select the '100si' variable (100m wind speed)
# Select the nearest grid points for the specified latitudes and longitudes
wind_speeds_da = ds["100si"].sel(
    latitude=xr.DataArray(lats, dims="location"), 
    longitude=xr.DataArray(longs, dims="location"), 
    method="nearest"
)

# Load the data into memory (computes the result)
print("Selecting points and computing data...")
wind_speeds_computed = wind_speeds_da.compute()
print("Computation complete.")

# --- Final Output ---
# Convert to DataFrame for display
wind_speeds_df = wind_speeds_computed.to_dataframe()

# Add location names for clarity (optional)
# This requires careful index matching if the original dims were kept
# Resetting index makes it easier
wind_speeds_df = wind_speeds_df.reset_index()

# Calculate valid time (similar to API example, using the dataset's time coordinate)
wind_speeds_df["ValidTime_UTC"] = pd.to_datetime(wind_speeds_df["time"]) + pd.to_timedelta(
    wind_speeds_df["prediction_timedelta"], unit="h"
)

# Map integer location index back to names if needed, or handle differently
# For simplicity, let's just print the DataFrame as is after time calc
final_df = wind_speeds_df[['time', 'prediction_timedelta', 'latitude', 'longitude', '100si', 'ValidTime_UTC']]


print(f"\nWind speed forecast from Zarr (Init: {init_time_str}):")
print(final_df)

# Close the dataset
ds.close()

Key Concepts Demonstrated

  • Authenticating direct Zarr access using aiohttp.BasicAuth within xarray.open_mfdataset's storage_options.

  • Constructing URLs for specific forecast initializations and lead time Zarr files.

  • Opening multiple remote Zarr files as a single virtual dataset using xr.open_mfdataset.

  • Selecting data for multiple specific latitude/longitude points from gridded data using xarray.sel with method="nearest".

  • Leveraging Dask for parallel computation via parallel=True and .compute().

  • Extracting and processing the resulting xarray.DataArray, including converting to a Pandas DataFrame and calculating valid timestamps.

</rewritten_file>

Last updated